\documentclass[oneside]{ctexbook}%基本文档类型是ctex article
% [oneside]选项的作用是，防止\part页面的前后出现空白页，因为书籍类型有内置的设置，就是让part前后生成1个空白页
\input{format.tex}  % 插入格式文件
% ------------------------------------------------------------ %
% 文档开始
% ------------------------------------------------------------ %
\begin{document}
\title{C++笔记}  % 封面标题
\author{taoyinw@foxmail.com}  % 作者
\date{\today}  % 封面日期
\maketitle  %生成封面

% 双栏目录
\begin{multicols}{2}
    \tableofcontents  %输出目录
\end{multicols}
% 单栏目录
%\tableofcontents  %输出目录

\pagestyle{headings}%fancyhdr宏包的内容，headings格式下，页脚是空白，页眉左侧是当前section内容，右侧是页码
\setcounter{page}{1}%从下面开始编页码
% 下面书写正文
% ------------------------------------------------------------ %
\part{C++基础}
\chapter{C++入门}

\subsection{写在前面}

区分大小写。

函数声明(function declaration)。

函数定义(function definition)包括函数头(function head)、函数体(function body)两部分。

代码中不可分割的部分称为token。

空格、制表符、回车统称为空白(white space)。

token必须用white space分隔开。

与函数名相关的圆括号的左右不要有white space，比如\verb|sum(a)|，尽量不要写成\verb|sum (0)|这种形式。
	
\subsubsection{注释}

C++支持C语言中的注释方式，但是一般来说建议按照C++的风格来写注释。

C++的注释以\verb|//|开始，到行尾结束，仅对本行有效。

C的注释以\verb|/*|开始，以\verb|*/|结束。

\subsubsection{命名空间(namespace)}

命名空间(namespace)是C++语言的一项特性。

C语言中与命名空间所类似的一个概念是作用域。但是其主要区别在于，C语言中不能自定义命名空间，而C++中，可以自定义命名空间的名称。这一点有利于代码的扩展与复用。

\subsubsection{运算符重载}

\verb|<<|表示信息流动的路径，也是C++对于按位左移运算符的一次"运算符重载"

其实，C语言本身也有许多运算符重载的例子，比如\verb|&|有时是取址运算符，有时是按位与运算符，\verb|*|有时是取值运算符，有时是乘法运算符。

编译器可以根据运算符的上下文来确定该运算符的具体含义。

\subsubsection{声明与赋值}

声明语句的作用是，编译器遇到声明语句之后，会为其声明的变量、函数等，分配相应的内存空间。

定义声明语句(defining declaration)，简称为定义(definition)。

在C语言中，所有的声明一般内存位于函数或过程的开始位置。

而在C++中，通常只在首次使用它的时候才进行声明。这样做也有确定啊，比如维护的时候容易找不到声明的位置。

赋值语句的作用是将值赋给内存单元。

\chapter{简单数据类型}
\section{变量}
\subsubsection{变量名的规定}
\begin{enumerate}
    \item 只能用字母，数字，下划线
    \item 第一个字符不能是数字
    \item 区分大小写
    \item 不能使用C++关键字
    \item 以1个或2个下划线开头的命名，是被保留的，是不建议使用的，可能留给编译期或者库来使用了。为了确保程序的可移植性，自己开发的时候尽量避免使用这样的变量名。(但是在类内部，或者某些可控的命名空间或者作用域中，可以使用)
    \item C++本身对变量命名的长度没有限制，但是有些平台可能会有限制。在ANSI C(C99)中，变量名只看前63个字符(前63个字符相同的变量名，编译器就会认为他们是同一个变量)。这也是C++与C的不同点。
\end{enumerate}

\section{整型数据}
\subsubsection{表示整型的类型}
整型就是没有小数部分的数字量。C++的标准整型类型是\verb|int|。

在此基础上，有符号性修饰符(signedness modifier)和大小修饰符(size modifier)。

使用任何一种修饰符，都可以省略关键字\verb|int|。

\subsubsection{符号性修饰符}
\begin{enumerate}
    \item \verb|signed|，有符号整型数
    \item \verb|unsigned|，无符号整型。
\end{enumerate}

\subsubsection{大小修饰符}
\begin{enumerate}
    \item \verb|short|，至少16位的宽度。
    \item \verb|long|，至少32位的宽度。
    \item (C++11)\verb|long long|，至少64位的宽度。
\end{enumerate}

可见，C++标准只是给出了每种整型类型最低的宽度，而具体有多少宽度，还是取决于编译器和机器，详见第\ref{数据模型(data models)}节。

\subsubsection{数据模型(data models)}\label{数据模型(data models)}
数据模型有LP32，ILP32，LP64，LLP64，ILP64这几种。32和64分别表示32位系统和64位系统。

\subsubsection{字母含义}
\begin{itemize}
    \item LP表示long和pointer
    \item ILP表示int, long和pointer
    \item LLP表示long long和pointer
\end{itemize}

\subsubsection{数据类型的宽度}
32位系统下
\begin{itemize}
    \item LP32，又称为2/4/4，\verb|int|类型16位，\verb|long|和指针32位。主要用于win16 API
    \item ILP32，又称为4/4/4，\verb|int|，\verb|long|和指针都是32位。主要用于win32 API，UNIX和类UNIX系统。
\end{itemize}

64位系统下
\begin{itemize}
    \item LLP64，又称为4/4/8，\verb|int|和\verb|long|是32位，指针是64位。主要用于win32 API，64位arm，x86-64
    \item LP64，又称为4/8/8，\verb|int|32位，\verb|long|和指针是64位。主要用于UNIX和类UNIX系统。
\end{itemize}

\subsubsection{其他} 
\begin{itemize}
    \item 整数算术运算符，对于有符号数和无符号数，的定义是不同的。
    \item \verb|std::size_t|是 \verb|sizeof| 运算符还有 \verb|sizeof...| 运算符及 \verb|alignof| 运算符 (C++11 起)的结果的无符号整数类型。
    \item (C++11)扩展整数类型(Extended integer types)
\end{itemize}

\section{字符类型(character type)}
字符类型，是一种特殊的整型类型，是用于表示字符的整型类型。

字符类型也有符号性修饰符\verb|signed|和\verb|unsigned|

\verb|char|的符号性取决于编译器和目标平台：ARM 和 PowerPC 的默认设置通常没有符号，而 x86 与 x64 的默认设置通常有符号。

\subsubsection{char类型的宽度} 实际上C++标准中并没有规定\verb|char|类型的宽度，与整型类型一样，标准中只规定了最低宽度。

\verb|char|类型的宽度是1个字节，\verb|signed char|可以表示的范围是-127~+128，\verb|unsigned char|可表示的范围是0~+255

\subsubsection{C语言的char字符} 在C中，单个字符常量以\verb|int|类型存储

\subsubsection{宽字符wchar\_t(wide character)}
\subsubsection{(C++11)char16\_t, char32\_t} \verb|char16_t|表示UTF-16字符，\verb|char32_t|表示UTF-32字符。
\subsubsection{(C++20)char8\_t} \verb|char8_t|表示UTF-8字符

\subsubsection{通用字符名(universal character name)}
\verb|\u|后面接4位16进制数，\verb|\U|后面接8位16进制数。不能多也不能少。

这里的16进制数，所表示的值均为Unicode码点值(Unicode code point)

\section{bool类型}
C++将非零值解释为\verb|true|，将0解释为\verb|false|。任何非零数字值和指针值都可以被隐式转换为\verb|bool|类型(不发生显示类型转换)。

字面值\verb|true|和\verb|false|都可以类型提升，转换为\verb|int|，其中\verb|true|转换为1，而\verb|false|转换为0。

\section{cv类型限定符}
cv类型限定符，主要指的是\verb|const|和\verb|volatile|这两个，主要功能是声明某个变量的常量性(constness)或易变性(volatility)。

每个cv类型限定符，只能出现1次。形如\verb|const volatile const|这样的写法是不合法的。

\subsection{类型限定符const}
\verb|const|关键字的作用是，使得该变量无法被修改。

\subsubsection{const变量的声明}
在声明时直接对\verb|const|的变量初始化，而不能留到下一个语句再进行赋值(因为变量不能再被修改了)。

\subsubsection{尽量使用const} C++开发中，如果想要定义符号常量，更建议使用\verb|const|，而不再使用传统C语言中的\verb|#define|。

因为\verb|const|+类型的形式，是处于C++类型检查的大框架之下的，享受C++在编译期进行的各种类型推断(包括以后C++标准增加的新规则)的益处的。而\verb|#define|仅仅做字符替换。相比之下，前者具有更多的编译期安全保障，有些错误可能会让编译器提前报告出来。

\subsubsection{const与指针}
\verb|const|放在指针类型后面，表示指针本身是常量，不可变。
\begin{lstlisting}
char* const pContent;
\end{lstlisting}

\verb|const|放在指针类型的前面，表示指针指向的数据是常量，不可变。但是可以换用另外一个指针指向这一块表示常量数据的内存。

\begin{lstlisting}
const char *pContent;
\end{lstlisting}

\subsubsection{头文件limits}
头文件\verb|limits|，旧版c++中使用\verb|limits.h|，在这一头文件中配置了关于整型数据的一些限制信息。

比如\verb|INT_MAX|为\verb|int|的最大取值，\verb|CHAR_BIT|为字节的位数等。

\subsection{类型限定符volatile}
由于编译器优化、操作系统的优化等环节，程序的某些内容可能会发生变化，而且这种变化，在引入\verb|volatile|关键字之前，是程序员单单从开发语言层面无法控制的(由于分层和封装的特性)。\verb|volatile|关键字就是为了防止这类现象的发生。

遇到这个关键字声明的变量，编译器对访问该变量的代码就不再进行优化。

\subsubsection{保证对特殊地址的稳定访问}
在下面的代码示例中，\verb|volatile|指出 i 是随时可能发生变化的，每次使用它的时候必须从 i的地址中读取，因而编译器生成的汇编代码会重新从i的地址读取数据放在 b 中。而优化做法是，由于编译器发现两次从 i读数据的代码之间的代码没有对 i 进行过操作，它会自动把上次读的数据放在 b 中。而不是重新从 i 里面读。这样以来，如果 i是一个寄存器变量或者表示一个端口数据就容易出错，所以说 \verb|volatile| 可以保证对特殊地址的稳定访问。
\begin{lstlisting}
volatile int i=10;
int a = i;
...
// 其他代码，并未明确告诉编译器，对 i 进行过操作
int b = i;    
\end{lstlisting}

\subsubsection{volatile与重排序}
\verb|volatile|的设计目的是为了让内存IO和信号处理变得可靠，它能保证的其实是修饰的变量无法被cache缓存，而必须在内存中进行读写。但是他没有保证指令不会被重排序破坏其原始顺序，总之C++标准中并不包含这样的保证。

\subsubsection{volatile与指针}
\verb|volatile|在指针类型的前面，表示指针指向的数据是\verb|volatile|的

\begin{lstlisting}
volatile char* vpch;    
\end{lstlisting}

\verb|volatile|在指针类型的后面，表示指针本身是\verb|volatile|的

\begin{lstlisting}
char* volatile pchv;
\end{lstlisting}

其他需要注意的地方：
\begin{enumerate}
    \item 可以把一个非\verb|volatile|的\verb|int|赋给\verb|volatile int|，但是不能把非\verb|volatile|对象赋给一个\verb|volatile|对象。
    \item 除了基本类型外，对用户定义类型也可以用\verb|volatile|类型进行修饰
    \item C++中一个有\verb|volatile|标识符的类只能访问它接口的子集，一个由类的实现者控制的子集。用户只能用\verb|const_cast|来获得对类型接口的完全访问。此外，\verb|volatile|向\verb|const|一样会从类传递到它的成员。
\end{enumerate}

\subsubsection{多线程下的volatile}
当两个线程都要用到某一个变量且该变量的值会被改变时，应该用 \verb|volatile| 声明，该关键字的作用是防止优化编译器把变量从内存装入 CPU 寄存器中。利用的是\verb|volatile|限定符保证对特殊地址的稳定访问的性质。如果变量被装入寄存器，那么两个线程有可能一个使用内存中的变量，一个使用寄存器中的变量，这会造成程序的错误执行。\verb|volatile| 的意思是让编译器每次操作该变量时一定要从内存中真正取出，而不是使用已经存在寄存器中的值。

\paragraph{存疑} 但是多线程下\verb|volatile|关键字的具体作用，网上也有不同的见解。

\href{https://zhuanlan.zhihu.com/p/62060524}{知乎原文链接} \textit{但是MESI协议能保证多核cache和主存的一致性，但现在CPU架构为了不浪费算力，把很多同步处理改成异步，例如cache line处于S状态，当前核修改数据，把cacheline状态修改成M，同时对其他核发送invalid消息，其他核收invalid把自己缓存的cacheline置失效。这个同步等待大大浪费CPU的算力，现在CPU解决这个问题引入了store buffer，相当于先把修改后数据存入store buffer，当收到其他核invalid ack后再写入cacheline,在这个过程中流水线继续处理其他指令，给人的错觉是执行时候指令乱序，重点还是可见性的问题。所以现在并发编程的挑战是内存可见性问题，volatile没有解决在弱内存可见性问题。}

\section{浮点型}
浮点类型\verb|float|、\verb|double|、\verb|long double|

\subsubsection{科学计数法，e表示法} \verb|2.52e+8|表示$ 2.52\times 10^8 $

\subsubsection{浮点类型的宽度}
\begin{enumerate}
    \item \verb|float|至少32位
    \item \verb|double|至少48位，而且实际实现的宽度不得少于\verb|float|
    \item \verb|long double|至少64位，而且实际实现的宽度不得少于\verb|double|
\end{enumerate}

通常，\verb|float|为32位，\verb|double|为64位，\verb|long double|为80, 96或者128位。

这3种类型的指数范围从-37到37

\subsubsection{头文件cfloat}
可以从头文件\verb|float.h|中查询到系统对浮点数的具体限制。

头文件\verb|cfloat|是头文件\verb|float.h|的C++版本。

\subsubsection{浮点字面值}
在代码中直接书写浮点字面值时，程序会将其存储为\verb|double|类型。

如果希望把他存储为\verb|float|类型，要在字面值后面加上\verb|f|或者\verb|F|后缀。\verb|long double|对应的后缀是\verb|l|或者\verb|L|。

\section{运算符, 算术运算符}
\subsubsection{运算符总述}
\subsubsection{运算符分类}
C++标准中的运算符包含许多种类，算术运算符只是其中的一部分，本节主要介绍算术运算符。
\begin{enumerate}
    \item 赋值运算符(assignment)
    \item 自增自减运算符(increment/decrement)
    \item 算术运算符(arithmetic)
    \item 逻辑运算符(logical)
    \item 比较运算符(comparision)
    \item 成员访问运算符(member access)
    \item 其他运算符(other)
\end{enumerate}

\subsubsection{其他运算符}
\begin{enumerate}
    \item 函数调用(function call)，圆括号运算符\verb|a(...)|
    \item 逗号运算符(comma)\verb|a, b|
    \item 条件运算符(conditional)\verb|a ? b : c|
    \item \verb|static_cast| 转换一个类型为另一相关类型
    \item \verb|dynamic_cast| 在继承层级中转换
    \item \verb|const_cast| 添加或移除 cv 限定符
    \item \verb|reinterpret_cast| 转换类型到无关类型
    \item \verb|new| 创建有动态存储期的对象
    \item \verb|delete| 销毁先前由 new 表达式创建的对象，并释放其所拥有的内存区域
    \item \verb|sizeof| 查询类型的大小，详见第\ref{sizeof}节
    \item (C++11)\verb|sizeof...| 查询形参包的大小，详见第\ref{sizeof...}节
    \item \verb|typeid| 查询类型的类型信息
    \item (C++11)\verb|noexcept| 查询表达式是否能抛出异常
    \item (C++11)\verb|alignof| 查询类型的对齐要求
\end{enumerate}

\subsubsection{算术运算符}
四则运算\verb|+-*/|

\verb|%|求模运算符，取余运算符，要求前后两个操作数必须为整型，例如\verb|19 % 6|得到1

\subsubsection{除法运算符}
除法运算的具体行为取决于操作数的类型。
\begin{itemize}
    \item 如果两个操作数都是整型，那么进行的是整型除法，结果的小数部分会被抛弃。
    \item 如果有一个(或者2个)操作数是浮点型，则结果也用浮点型来表示。
\end{itemize}

\subsubsection{求模运算符}
求模运算符返回整型除法的余数。

\subsubsection{sizeof}\label{sizeof}
\verb|sizeof|是C语言中的一个运算符，也是比较少见的全部由字母组成的运算符。

他的功能，简单来说，是返回一个对象或类型所占的内存字节数。

用法。注意，与表达式连用时，注意运算符优先级，如果担心发生危险则可加上括号。
\begin{lstlisting}
sizeof( type )
sizeof expression
\end{lstlisting}

\verb|sizeof|返回的字节，其具体包含的bit数可能因机器而已。具体在当前机器中，一个字节包含多少bit，可以使用宏定义\verb|CHAR_BIT|查看，定义在头文件\verb|<climits>|中。

但是有一些写法固定会返回1，这也是cppreference中给出的。
\begin{lstlisting}
sizeof(char)
sizeof(signed char)
sizeof(unsigned char)
sizeof(std::byte)  // (since C++17)
sizeof(char8_t)  // (since C++20)
\end{lstlisting}

另外在处理字符串的时候，\verb|sizeof|往往与\verb|strlen()|有相似的适用场景，具体区别可查阅第\pageref{sizeof和strlen()的区别}页第\ref{sizeof和strlen()的区别}节的内容。

\subsubsection{(C++11)sizeof...}\label{sizeof...}
返回一个参数包中元素的个数。

\begin{lstlisting}
#include <array>
#include <iostream>
#include <type_traits>

template<typename... Ts>
constexpr auto make_array(Ts&&... ts)
{
    using CT = std::common_type_t<Ts...>;
    return std::array<CT, sizeof...(Ts)>{std::forward<CT>(ts)...};
}

int main()
{
    std::array<double, 4ul> arr = make_array(1, 2.71f, 3.14, '*');
    std::cout << "arr = { ";
        for (auto s{arr.size()}; double elem : arr)
        std::cout << elem << (--s ? ", " : " ");
        std::cout << "}\n";
}


// Output:
// arr = { 1, 2.71, 3.14, 42 }
\end{lstlisting}

\section{类型转换}\label{类型转换}
\subsubsection{类型初始化}
\subsubsection{基本数据类型之间的类型转换}
C++允许将一种类型的值赋值给另一种类型的变量，值将自动被转化为接受它的变量的类型。

\subsubsection{数据精度变化}
将较大的浮点类型转换为较小的浮点类型，比如将\verb|double|转变为\verb|float|，首先，精度会降低，值可能超出目标类型的取值范围，此处可能发生UB。

将浮点类型转换为整型，小数部分会直接丢失。如果超出取值范围时，也会发生UB。

将较大的整型转换为较小的整型，通常只取靠右边的字节。

\subsubsection{(C++11)列表初始化}
\subsubsection{(C++11)以花括号进行初始化时}
C++11将使用花括号的初始化称为列表初始化(list-initialization)，因为这种情况常常发生在给一个复杂的数据结构提供一个列表以供其初始化。

\subsubsection{区别}
对类型转换的要求更加严格。不允许数据截断或缩窄(narrowing)，因此禁止了某些隐式类型转换的情形，编译阶段就会报错。

\begin{enumerate}
    \item 从浮点类型向整型的转换，反过来也是禁止的。
    \item (C++23之前)从\verb|long double|向\verb|double|，从\verb|double|向\verb|float|，除非来源是常量表达式且不发生溢出。
    \item 从整数或无作用域枚举类型到不能表示原类型所有值的整数类型的转换(conversion from integer or unscoped enumeration type to integer type that cannot represent all values of the original)，简单来说，就是枚举类型太多，超出某个较窄的整型的取值范围时，这时也会发生缩窄。
    \item 从指针或成员指针类型到\verb|bool|的转换
\end{enumerate}

代码示例

\begin{code_example}
\label{使用花括号进行变量初始化} 
\begin{lstlisting}
const int code = 66;
int x = 66;
char c1 = { 31325 };  // 不合法，因为会发生缩窄(narrowing)
char c2 = { 66 };  // 合法，因为char型变量可以接收整型的66
char c3 = { code };  // 
char c4 = { x };  // 不合法，见正文
x = 31325;
char c5 = x;
\end{lstlisting}
\end{code_example}

在\verb|char c4 = { x };| 这里，因为编译器只意识到\verb|x|是一个变量，而不知道他实际上存储的值的长度，所以这时使用列表初始化是不允许的，因为也可能\verb|x|里面保存了一个很大的量，\verb|char|类型的\verb|c4|可能保存不下。

\subsubsection{列表初始化的类型}
列表初始化不是表达式，也不具有类型，所以形如\verb|decltype({1, 2});|这样的表达式是不合理的。

没有类型，也就意味着模板类型推导也会失效，没能提供足够充分的类型信息来进行类型推导。对于某个给定的模板\verb|template<class T> void f(T);|来说，\verb|f({1, 2, 3})|也是不合理的。

\subsubsection{列表初始化与聚合体(aggregate)}
聚合体直接从同类型的单元素花括号初始化列表﻿进行复制/移动初始化，但非聚合体首先考虑 \verb|std::initializer_list|构造函数

\begin{lstlisting}
struct X {}; // 聚合体

struct Q     // 非聚合体
{
    Q() = default;
    Q(Q const&) = default;
    Q(std::initializer_list<Q>) {}
};

int main()
{
    X x;
    X x2 = X{x}; // 拷贝构造函数(非聚合初始化)
    
    Q q;
    Q q2 = Q{q}; // initializer_list 构造函数(非拷贝构造函数)
}
\end{lstlisting}

\subsubsection{表达式中的转换}
\subsubsection{整型提升(integral promotion)}
\paragraph{整型提升1}
在计算表达式时，编译器将\verb|bool|，\verb|char|，\verb|unsigned char|，\verb|signed char|和\verb|short|值转换为\verb|int|。\verb|true|被转换为1，\verb|false|被转换为0。这一类转换称之为整型提升

可以看代码示例\ref{整型提升}

\begin{code_example}
\label{整型提升}
\begin{lstlisting}
short chickens = 20;
short ducks = 53;
short fowl = chickens + ducks;
\end{lstlisting}
\end{code_example}

在计算第3行语句中的表达式的时候，编译器将\verb|chickens|和\verb|ducks|先从\verb|short|类型转换为\verb|int|类型，进行计算，再将结果转换为\verb|short|类型再赋值给一个\verb|short|变量。

\paragraph{整型提升2}
如果\verb|short|比\verb|int|短，则\verb|unsigned short|被转换为\verb|int|，如果\verb|short|和\verb|int|一样长，则转换为\verb|unsigned int|。这是为了确保转换不会损伤精度，不会破坏数据。

\paragraph{整型提升3}
\verb|wchar_t|也会被转换为宽度足够存储\verb|wchar_t|的数据类型，\verb|int|，\verb|unsigned int|或者\verb|unsigned long|。

通常\verb|int|类型选择为计算机最自然的类型，计算机采用这一类型进行运算的时候速度最快。

\subsubsection{不同类型进行算术运算时}
在不同类型的数据进行算术运算的时候，较小的类型将会被转换为较大的类型。

\subsubsection{传递参数时发生的类型转换}
正如第\ref{函数}章将介绍的，传递参数时的类型转换通常由C++函数原型控制。然而，也可以取消原型对参数传递的控制，尽管这样做并不明智。在这种情况下，C++将对\verb|char|和\verb|short|类型(\verb|signed|和\verb|unsigned|)应用整型提升。另外，为保持与传统C语言中大量代码的兼容性，在将参数传递给取消原型对参数传递控制的函数时，C++将\verb|float|参数提升为\verb|double|。

\subsubsection{强制类型转换}\label{强制类型转换}
C++允许通过强制类型转换机制，显式地进行类型转换。

\subsubsection{强制类型转换的形式}
强制类型转换，可以使用以下两种格式

\begin{lstlisting}
(typename) value
typename (value)
\end{lstlisting}

第一种格式来自于C语言，第二种格式是C++独有的，想法是让强制类型转换具有函数调用的形式，这样就使得，对于自带的内置类型的强制类型转换，和用户为类设计的类的类型转换，具有极其相似的外观。

\subsubsection{强制类型转换运算符}
C++还引入了4个强制类型转换运算符，\verb|dynamic_cast|，\verb|const_cast|，\verb|static_cast|和\verb|reinterpret_cast|。其介绍请见第\ref{类型转换运算符}节。

\subsubsection{其他的类型转换情况分析}
与类有关的类型转换，请查阅第\ref{和类有关的数据类型转换问题}节。


\chapter{复合数据类型}

\section{数组}

数组，存储多个同类型的值。

\subsubsection{数组的声明}

C++声明数组的方式，与C一致。

\begin{lstlisting}
typename arrayName [arraySize];
\end{lstlisting}

其中\verb|arraySize|必须是常量或者表达式

数组一定要基于基本类型存在，没有脱离了基本类型的数组，比如提到一个数组，一定要带上基本类型，比如说，他是\verb|int|数组，或者他是\verb|float|数组。这也是数组为什么是一个复合类型的原因。

\subsubsection{数组名}

C++将数组名解释为第一个元素的地址。

数组名是指向数组中第一个元素的指针

\verb|& 数组名| 返回整个数组的地址

\subsubsection{角标，index}

角标(index)从0开始，最后一个元素的index比数组长度少1

数组元素用花括号括起来

\subsubsection{二维数组}

C++本身没有规定二维数组这一数据格式，但是可以创建由数组组成的数组，这样就构成了二维数组。

那么同理，多维数组也可以得到。

\verb|int maxtemps[4][5]|表示4个元素的数组，每个元素是5个元素长度的数组。表示了一个$ 4\times 5 $的矩阵。

\verb|maxtemps[0]|表示5个int组成的数组

\verb|maxtemps[0][0]|表示1个int值

循环二维数组

\begin{lstlisting}
for (int row = 0; row < 4; row++)
{
	for (int column = 0; column < 5; ++colomn)
	{
		statement(s);  // 循环体
	}
}
\end{lstlisting}

在上面的代码中，\verb|row++|表示先用值再递增，\verb|++colomn|表示先递增再用值。

但是这里的区别，还不清楚。(疑问)

\section{字符串}

字符串，连续存储\verb|char|类型变量

\subsubsection{C语言的传统字符串}

\subsubsection{字符串是字符数组}

C语言中字符串指的就是\verb|char[]|数组

以空字符\verb|'\0'|结尾

声明方式，见下面的代码示例

\begin{lstlisting}
char bird[11] = "Mr. Cheeps";
char fish[] = "Bubbles";  // 让编译器自动添加字符串结尾\0
\end{lstlisting}

\subsubsection{字符串常量}

上面的\verb|"Bubbles"|，称之为字符串常量(string constat)，或者字符串字面值(string literal)

两个中间用空格分隔的字符串常量，编译器会自动将之串联起来。

\subsubsection{字符串长度}

\begin{lstlisting}
char bird[11] = "Mr. Cheeps";
strlen(bird)
sizeof(bird)
sizeof bird
\end{lstlisting}

\subsubsection{sizeof和strlen()的区别}\label{sizeof和strlen()的区别}
\verb|sizeof|可以用作运算符，可以看做一个函数，因为使用上比较像。在这个应用中，\verb|sizeof|返回数组占据的总长度，对于\verb|char[]|数组来说，当然是包含空字符\verb|'\0'|的

\verb|strlen()|函数在头文件\verb|string.h|中，只返回字符数组中使用了的长度，如果数组的大小是100但是却只存储了5个字符，则会返回5而不是100。不包含空字符\verb|'\0'|。

\verb|sizeof|是运算符，所以他在编译期就已经确定下来其结果了。而\verb|strlen()|作为函数，在运行时才能得到结果。(不过这个说法并未得到验证，也可能与编译器实现与优化有关。)

\subsubsection{字符串与数值之间的转换}

\verb|atof|将字符串转换为浮点数

\verb|atoi, atol, atoll(C99)|将字符串转换为\verb|int, long, long long|

\verb|strtol, strtoll(C99)|，根据给出的进制来转换为\verb|long|和\verb|long long|

\begin{lstlisting}
long strtol( const char *str, char **str_end, int base );
\end{lstlisting}

其中第二个参数\verb|str_end|的作用是，指向未能转换为整数的字符串剩下的部分。比如说传入的字符串是1234abcd，转换为10进制整数，则转换后得到1234，而\verb|str_end|会指向a的位置

\verb|strtoul, strtoull(C99)|，同理，转换为无符号整数

\verb|strtof(C99), strtod, strtold(C99)|，转换为浮点数

\subsubsection{其他字符串函数}

在头文件\verb|string.h|中定义了许多对字符串进行处理的函数

\subsubsection{strcat()}

将两个字符串拼接起来，将第二个字符串拼接到第一个字符串的末尾。

由函数原型可见，其接受的参数都是\verb|char*|

\begin{lstlisting}
char *strcat( char *dest, const char *src );  // (until C99)
char *strcat( char *restrict dest, const char *restrict src );  //(since C99)
\end{lstlisting}

\subsubsection{strcmp()}

对比两个字符串，是以逐个字符的方式，比较其在ASCII码中的顺序。

compares two null-terminated byte strings lexicographically.其中，null-terminated的意思是以空字符\verb|'\0'|结尾的

如果两个字符串每个字符都完全相等，则返回0。

\begin{lstlisting}
int strcmp( const char *lhs, const char *rhs );
\end{lstlisting}

\subsubsection{strcpy()}
将字符串\verb|src|复制到\verb|dest|中

复制过程是包括串\verb|src|里的空字符\verb|'\0'|的

如果\verb|dest|的大小不够的话，会发生未定义行为(undefined behavior)。\verb|strcpy| 函数没有方法来保证有效的缓冲区尺寸，所以它仅仅能假定缓冲足够大来容纳要拷贝的字符串。在程序执行时，这将导致不可预料的行为，容易导致程序崩溃。
\begin{lstlisting}
char * strcpy( char *dest, const char *src );  // (until C99)
char * strcpy( char *restrict dest, const char *restrict src );  // (since C99)
\end{lstlisting}

\subsubsection{strncpy()}
把 src 所指向的字符串复制到 dest，最多复制 n 个字符。

当 src 的长度小于 n 时，dest 的剩余部分将用空字节填充。

该函数返回最终复制的字符串。
\begin{lstlisting}
char * strncpy(char *dest, const char *src, size_t n)
\end{lstlisting}

\subsubsection{strcpy\_s()}
\verb|strcpy_s|函数可以通过设置目标缓冲区大小来够避免缓冲溢出导致的未定义行为(undefined behavior)。

\verb|strcpy_s|函数在拷贝过程中，如果遇到\verb|'\0'|结束符，那么直接结束拷贝。

\begin{lstlisting}
errno_t strcpy_s( char *restrict dest, rsize_t destsz, const char *restrict src );  // since C11
\end{lstlisting}

\subsubsection{sprintf()}

这个函数在\verb|stdio.h|中声明的，是向第一个参数的字符串中写入，而不是向标准输出流中写入。

\begin{lstlisting}
int sprintf( char * buffer, const char * format, ... );
\end{lstlisting}

\subsubsection{string类}

\subsubsection{string类的使用方法}

string类，定义在头文件string中。

string类位于命名空间std中，因此必须使用using编译指令，或者使用std::string来使用它。

string类定义隐藏了字符串年的数组性质，更方便。

可以使用cin将键盘输入储存到string对象中

可以通过cout来显示string对象

可以使用数组表示法来访问存储在string对象中的字符。例如\verb|string str2 = "panther";|，则\verb|str2[0]|为字符p

string类简化了字符串合并操作，可以使用运算符\verb|+|来将两个字符串合并起来。

\begin{lstlisting}
	string str3;
	str3 = str2 + str1;
	/* 或者也可以这样用 */
	str1 += str2;  // 把str2放到str1的尾部
\end{lstlisting}

\subsubsection{string类与C风格字符串的区别}

程序可以自动给处理string的长短，而数组的长度是固定的。

不能将一个数组赋值给另一个数组，但是可以将一个string对象赋值给另一个string对象

string类的合并操作，使用\verb|str3 = str2 + str1;|的方法。而同样操作，对于C风格字符串，需要使用

\begin{lstlisting}
	strcpy(charr3, charr1);
	strcpy(charr3, charr2);
\end{lstlisting}

使用C风格字符串时，由于他本质上是一种数组，所以存在目标数组过小，无法存储指定信息的情况存在。而string类具有自动调整大小的功能，可以避免这些情况发生。

\subsubsection{宽字符wchar\_t}
宽字符类型\verb|wchar_t|与\verb|char|相比，他占用更多的存储空间，一般是2或者4字节，与编译器的具体实现方式有关。他可以用来存储更广泛的字符集。宽字符类型\verb|wchar_t|常被用来处理多语言环境中的字符数据，在国际化和本地化的功能开发中有广泛应用。

\paragraph{头文件}
\verb|<cwchar>|在C++98中就已加入了标准。C语言对应的头文件是\verb|<wchar.h>|，在C95中引入。

\section{结构(struct)}

\subsubsection{简介}

结构，用于存储不同类型的值。数组只能存储类型相同的值。

\subsubsection{结构的声明}

\begin{lstlisting}
struct structName
{
    typename1, memberName1;
    typename2, memberName2;
    ...
};  // 这里要有一个分号，注意。

// 使用结构
structName VariableName;  // 定义了结构后可以声明结构变量
// 结构变量指的是，属于这一结构的类型的变量
\end{lstlisting}

C++允许在声明结构变量时省略关键字\verb|struct|

成员运算符dot\verb|"."|

\subsubsection{结构数组}

结构数组，由结构组成的数组。是一个数组，数组里每一个元素都是结构。

\subsubsection{结构数组的声明}

\begin{lstlisting}
structName arrayName[Size];
\end{lstlisting}

数组\verb|arrayName|具有\verb|Size|个\verb|structName|类型的元素。

从\verb|arrayName[0]|一直到\verb|arrayName[Size-1]|

\subsubsection{结构中的位字段}

冒号加数组

\section{聚合类(aggregate class)}

\section{共用体(union)}

共用体(union)只能存储多个类型中的一种。

\verb|struct|是AND关系，\verb|union|是OR关系

匿名共用体(anonymous union)

\section{枚举(enum)}

将整数数值依次赋给枚举量(enumerator)，从0开始

\verb|enum|格式的变量只能有有限个值，而且只能取这些值

可以显式地给枚举量赋值，比如\verb|enum bits{one = 1, two = 2, four = 4};|

C++11扩展了枚举的用法，增加了作用域内枚举(scoped enumeration)

\section{指针}

\subsubsection{简介}

计算机管理数据，往往通过三个范畴，(where)存储在哪里，(what)存储什么，(type)类型是什么。

取值运算符\verb|&|

取值运算符\verb|*|，也称为间接值运算符(indirect value)，解除引用运算符(dereferencing)

使用常规变量时，值是指定的量，地址为其派生的量。

面向过程编程(POP)和面向对象编程(OOP)的区别在于，OOP强调在运行阶段作决策，POP强调在编译阶段作决策。

\subsubsection{指针的声明}

声明指针的时候，必须指定指针指向的数据类型。

\begin{lstlisting}
int * ptr;
\end{lstlisting}

\verb|ptr|是指针，指向一个\verb|int|类型的数据。

\verb|*|左右两边的空格都是可选的，但是注意，只作用于紧邻的后面的一个标识符。

下面的这种写法是错误的

\begin{lstlisting}
int * ptr1, ptr2;
\end{lstlisting}

指向不同数据类型的数据的指针，是不相同的指针。指针也是一种复合数据类型。

但是指针本身只是一个地址值，并不体现它指向的数据类型。"看上去一个样，但是实际上不一样"

\begin{lstlisting}
int * ptr1; char * ptr2;
\end{lstlisting}

\verb|ptr1|和\verb|ptr2|都是指针，但是确是不同类型的指针，他们两个在数据类型上来说，是不同的。

即，指针是基于基本类型的数据类型，指针是一种复合数据类型。

不能简单地将整数值赋值给指针。虽然指针在内存中也以一段数字来存储，但是不能通过直接赋给某一个整数值的方式来创建指针。

合理的做法如下。

\begin{lstlisting}
int * ptr;
pt = (int*) 0XB800 0000;
\end{lstlisting}

\subsubsection{空指针}\label{C++11中的空指针}
在C++98中，字面值0有两个含义，可以表示数字值0，也可以表示空指针。这就产生了二义性。

有些程序员使用\verb|(void*)0|来表示空指针，也有的使用\verb|NULL|来表示，这是一个C语言的宏定义。

C++11提供了更好的解决方案，引入了新关键字\verb|nullptr|来表示空指针的字面值，但是兼容了旧的用0表示空指针的方法。

\subsubsection{nullptr}
\verb|nullptr|是\verb|std::nullptr_t|的纯右值形式(纯右值的具体内容请参考第\ref{值类型(value categories)}节)。

\subsubsection{std::nullptr\_t}
\verb|std::nullptr_t|是空指针字面量\verb|nullptr|的类型。\verb|std::nullptr_t|本身不是指针类型。

\verb|sizeof(std::nullptr_t)|等于\verb|sizeof(void *)|

\begin{lstlisting}
using nullptr_t = decltype(nullptr);  // (since C++11)
\end{lstlisting}

\begin{lstlisting}
#include <cstddef>
#include <iostream>

void f(int*)
{
    std::cout << "整数指针重载\n";
}

void f(double*)
{
    std::cout << "double 指针重载\n";
}

void f(std::nullptr_t)
{
    std::cout << "空指针重载\n";
}

int main()
{
    int* pi{};
    double* pd{};
    
    f(pi);
    f(pd);
    f(nullptr); // 无 void f(nullptr_t) 可能有歧义
    // f(0);    // 歧义调用：三个函数全部为候选
    // f(NULL); // 若 NULL 是整数空指针常量则为歧义
    // (如在大部分实现中的情况)
}

// 代码output
// 整数指针重载
// double 指针重载
// 空指针重载    
\end{lstlisting}

\subsubsection{指针算数}
C++中指针和数组的相似性。

指针变量加1，其增加的值，等于其所指向的类型所占用的字节数。

当两个指针指向同一个地址时，指针之间可以相减，得到某一个整数值。

\verb|array[0]|等同于\verb|*array|

\verb|array[3]|等同于\verb|*(array+3)|

\subsubsection{指针与const}

\subsubsection{指向const或者非const变量的非const指针}

指向\verb|const|变量的指针，指向一个\verb|const|变量的指针，不能通过这个指针修改变量的值，因为变量是\verb|const|的。

虽然指向的变量不能改变，但是这个指针本身是可以改变的，他可以更改为指向其他的变量。但是，就算指向非\verb|const|变量，也不能通过这个指向\verb|const|变量的指针修改非\verb|const|变量的值。

相反，如果是一个指向非\verb|const|对象的指针，更改为指向\verb|const|对象，这个指针仍然能修改\verb|const|对象。

可以见得，指针保持自身的\verb|const|特性，而与其具体指向的对象的\verb|const|特性无关。

\subsubsection{const指针}

\verb|const|指针，指的是这个指针自身是不能被改变的，他不能更改指向，不能指向其他变量，只能指向初始化时的对象。但是其指向的内容，如果没有被声明为\verb|const|，那么是可以修改的。

\subsubsection{管理内存的3种方式}
\begin{enumerate}
\item \textbf{自动存储}：函数内部定义的常规变量，称为自动变量(automatic variable)，调用函数时产生，结束调用时消亡。自动变量是局部变量，作用域是所在的代码块。通常存储在栈中，后进先出，LIFO
\item \textbf{静态存储}：在函数外部定义，或者在声明时使用关键字\verb|static|来修饰。
\item \textbf{动态存储}：使用\verb|new|和\verb|delete|。自动存储空间(free store)或者堆(heap)
\end{enumerate}

\subsubsection{void*指针}
\verb|void*|是C++继承自C语言的遗产。\verb|void|表示类型不确定、未确定或无需确定。

\subsubsection{void的含义}
\verb|void|类型不能声明实例对象，例如\verb|void par = 10|这样的声明是绝对非法的。

\verb|void|用作函数的传入参数和返回值的类型，可以限制这个函数，让他不允许有传入参数或返回值。

\subsubsection{void*的含义}
\verb|void*|是可以用来声明实例对象的，例如下面的代码是合法的。

\begin{lstlisting}
int someInt = 10; void* par = &someInt;
\end{lstlisting}

因为无论指向什么类型的指针，指针本身所占空间是一定的。我们可以认为\verb|void*|就是一个通用指针，可以指向任意类型的指针。

指针的两大要素就是首地址和长度。而\verb|void*|作为一种能够指向任意类型的指针，就等价于，抹去了长度这个元素。这个元素在操作时需要程序员自己把控，或者依赖于其他的编码方式。

\begin{lstlisting}
void* memcpy(void* dest, const void* src, size_t len);
void* memset(void* buffer, int c, size_t num);
\end{lstlisting}

而对于C++的泛型编程特性来说，\verb|void*|也能体现出强大的共通性，或者说，与类型无关的特性。

\subsubsection{使用void*时的一些限制}

如果将\verb|void*|类型指针赋给其他类型指针，则需要强制类型转换

\begin{lstlisting}
int someInt = 10;
void* pvoid = &someInt;
int* pInt = (int*) pvoid;    
\end{lstlisting}

\verb|void*|指针不可解引用

\begin{lstlisting}
int someInt = 10;
void* pvoid = &someInt;
*pvoid ++;
\end{lstlisting}

若想正确删除掉\verb|void*|指向的动态类型变量，需要进行强制类型转换。因为\verb|void*|不带有长度信息，所以系统并不知道需要清除多长的内存。

\begin{lstlisting}
delete voidPointer; 					//只是清空了一个指针
delete (FrameInfo*) voidPointer; 		//正确析构voidPointer指向的变量。
\end{lstlisting}

\subsubsection{void*与模板}
\verb|void*|可以作为一种轻量化的泛型编程手段。

但是\verb|void*|也会将一些问题隐藏起来，在运行时暴露出来。而良好的泛型编程，应当是把这些问题在编译期暴露出来，以防止程序在运行时才暴露问题带来崩溃。

\section{new与delete}
\subsubsection{new表达式}
\verb|new|表达式，通过动态存储来创建并初始化对象。

\subsubsection{常用的形式}
\begin{lstlisting}
::(optional) new (type﻿) new-initializer ﻿(optional)  // (1)	
::(optional) new type new-initializer ﻿(optional)  // (2)	
::(optional) new (placement-args) (type) new-initializer ﻿(optional)  // (3)	
::(optional) new (placement-args﻿) type new-initializer ﻿(optional)  // (4)	
\end{lstlisting}

(1)和(2)，通过\verb|type|来创建对象，可能是数组类型，(C++11)可能包含占位符(placeholder type specifier)，(C++17)or include a class template name whose argument is to be deduced by class template argument deduction

(3)和(4)，是placement new用法。

\subsubsection{返回纯右值指针}
\verb|new|表达式，返回一个纯右值指针，指向刚刚被构造好的对象，如果创建的是数组类型，则指向数组第一个元素。

\subsubsection{建议加上圆括号}
在使用方法(1)和(3)中，建议\verb|type|位置的参数，要加上圆括号。
\begin{lstlisting}
new int(*[10])();    // error: parsed as (new int) (*[10]) ()
new (int (*[10])()); // okay: allocates an array of 10 pointers to functions
\end{lstlisting}

\verb|new|表达式，对于\verb|type|参数是采用贪婪地方式来处理的，就是尽可能将内容纳入到\verb|new|表达式当中。看下面的代码示例。
\begin{lstlisting}
new int + 1; // okay: parsed as (new int) + 1, increments a pointer returned by new int
new int * 1; // error: parsed as (new int*) (1)
\end{lstlisting}

\subsubsection{不合法的使用情况}
\begin{enumerate}
    \item \verb|type|参数是缺少边界的数组。
    \item \verb|type|参数中，使用了占位符，\verb|auto| 或 \verb|decltype(auto)|(C++14)，可能与类型约束结合(C++20)
    \item 类型中使用了需要推导参数的类模板(C++17)
\end{enumerate}

\begin{lstlisting}
double* p = new double[]{1, 2, 3}; // creates an array of type double[3]
auto p = new auto('c');            // creates a single object of type char. p is a char*

auto q = new std::integral auto(1);         // OK: q is an int*
auto q = new std::floating_point auto(true) // ERROR: type constraint not satisfied

auto r = new std::pair(1, true); // OK: r is a std::pair<int, bool>*
auto r = new std::vector;        // ERROR: element type can't be deduced
\end{lstlisting}

\subsubsection{与数组类型连用}
\verb|type|参数，除了第一个维度的其他维度，必须是正的整型常量表达式(C++14之前)，或者是，经转换的常量表达式(converted constant expression)表示的\verb|std::size_t|(C++14之后)。

第一个维度，可以是整型，枚举类型，或者是具有可显式转换为整形或枚举类型的转换函数的类(C++14之前)，或者是任意可被转换为\verb|std::size_t|的表达式(C++14之后)。

这是唯一一种，可以创建一个在运行时才确定大小的数组的方式。一般dynamic arrays说的就是这种使用情况。

加括号的使用方式(2)是不能应用于这种情况的。
\begin{lstlisting}
int n = 42;
double a[n][5]; // error
auto p1 = new  double[n][5];  // OK
auto p2 = new  double[5][n];  // error: only the first dimension may be non-constant
auto p3 = new (double[n][5]); // error: syntax (1) cannot be used for dynamic arrays
\end{lstlisting}

\subsubsection{分配过程}
\verb|new|表达式，是通过调用适当的分配函数(allocation functions)来进行分配内存空间的。

分配函数(allocation functions)指的就是一系列\verb|operator new|和\verb|operator new[]|函数。分配函数的第一个参数一般就是所需要的字节数\verb|std::size_t count|。
\begin{itemize}
    \item 如果\verb|type|参数是非数组类型，使用的函数是\verb|operator new|。
    \item 如果\verb|type|参数是数组类型，使用的函数是\verb|operator new[]|。
\end{itemize}

用户是可以用自己编写的全局的，或者类级别的分配函数来替换编译器给出的。但是如果\verb|new|前面使用了域解析运算符\verb|::|，那么类一级别的分配函数就会被忽略。

如果需要分配的类型，本身是一个类，那么会先在类的范畴内先搜索分配函数。

这种动态数组，还有一个额外的开销，有一块空间用来存放，这个数组的长度(元素的个数)。这样，\verb|delete[]|在析构这个数组的时候，也知道这个数组都占用了多少空间，正确的析构每一个元素。这也就是\verb|new[]|和\verb|delete[]|要成对使用的原因。

\subsubsection{new和new[]的区别}
使用\verb|new[]|创建的动态变量，必须使用\verb|delete[]|去释放。

对于\verb|new AA[10]|，必须使用\verb|delete[] AA|来释放。\verb|delete[]|会依次调用每个成员的析构函数去逐个释放。

使用\verb|new[]|的时候，返回的指针中，也同时使用4个字节，存储了方括号里传入的个数。这也就不难解释，为什么在\verb|delete[]|的时候，不用传给它对象个数。这样就说明了，\verb|new, delete|和\verb|new[], delete[]|要成对使用。

\subsubsection{placement new机制}
placement new 是 new 关键字的一种进阶用法，既可以在栈区(stack)上生成对象，也可以在堆区(heap)上生成对象。

相对应地，我们把常见的 new 的用法称为 operator new，它只能在 heap 上生成对象。

placement new 利用已经申请好的内存来生成对象，它不再为对象分配新的内存，而是将对象数据放在 address 指定的内存中。
\begin{lstlisting}
new(address) ClassConstruct(...) ; 
// address : 表示已有的内存地址，该地址可以在栈上，也可以在堆上
// ClassConstruct(...) : 表示调用类的构造函数，如果构造函数没有参数，也可以省略括号。
\end{lstlisting}

\subsubsection{malloc/free和new/delete的区别}
\verb|malloc|的原型\verb|void* malloc(size_t size)|，头文件\verb|stdlib|

\verb|free|的函数原型\verb|void free(void* ptr);|

\verb|sizeof|是C语言中的保留关键字，也可以看做单目运算符，返回值是字节数，单位是字节。

\begin{enumerate}
    \item \verb|malloc/free|是C的库函数，\verb|new/delete|是C++中的运算符。
    \item 一个是手动计算大小，一个是自动计算的。\verb|malloc|在使用时，需要用\verb|sizeof|计算出需要分配的内存空间的大小，并将之传递给\verb|malloc|。而\verb|new|在使用时不需要计算，传递给它一个已定义的类型名即可。
    \item 返回值的类型不同。\verb|malloc|返回值的类型是\verb|void*|，需要进行强制类型转换(详见下面的代码示例)。而\verb|new|返回的指针，其指向就是传入的类型。 
\begin{lstlisting}
char* a = NULL;
a = (char*)malloc(100*sizeof(char));
\end{lstlisting}
    \item 分配失败时的行为不同。\verb|malloc|分配失败时返回一个空指针。\verb|new|分配失败时会抛出异常。
    \item \verb|malloc|分配内存，如果小于128kb，使用\verb|brk|系统调用，在\textbf{虚拟内存}上分配的，是在\textbf{堆区域}分配的。如果大小超过了128kb，则可能会涉及到文件映射区和\verb|mmap|系统调用。\verb|new|是在自由存储区进行分配的。
    \item 工作过程上的不同，初始化内存的时机不同，内存的类型也不同。调用\verb|new|运算符时，首先申请足够的空间，然后调用构造函数以初始化成员变量(如果成员变量也有构造函数则递归地进行此过程)。而\verb|malloc|在申请空间时，申请的是虚拟内存，\textbf{并没有立刻拥有物理内存}，而且没有对其进行初始化。后面如果进行初始化的时候，可能需要通过页表来与物理内存之间建立映射关系(前提是发生了缺页中断)。
    \item \verb|delete|需要该对象类型的指针，而\verb|free|仅需\verb|void*|的指针。\verb|delete|是需要调用析构函数的。\verb|delete|的过程，是先调用成员的析构函数(如果成员变量也有析构函数则递归地进行此过程)，然后运行\verb|delete|运算符，然后释放内存空间。
\end{enumerate}

\subsubsection{free如何知道需要释放多大的内存空间？}
这与\verb|malloc|工作原理中的一个细节有关，他在申请内存空间的时候多申请了16字节，在这16个字节之后才是申请出来的内存段。\verb|malloc|返回的首地址，是16字节之后的位置。在\verb|free|的时候，则会向左偏移16个字节，读取其中的信息。这16个字节存储的是分配出的内存段的描述信息，其中就包含了内存段的长度。

\subsubsection{free释放后，内存的行为是什么？}
\verb|free|在释放内存时，如果是通过\verb|brk|系统调用分配的堆内存，则只是交还给进程的虚拟内存空间。如果是通过\verb|mmap|系统调用分配的内存，则操作系统会介入来回收这块内存。

\section{类型的组合}

结构

结构变量

结构变量的成员

结构数组：装着结构变量的数组

结构数组的成员是结构变量，结构变量也有其成员

结构指针：指向结构变量的指针，访问成员要用\verb|->|，而不能用句点\verb|.|

指针数组：装着指针的数组

指向指针数组的指针

\section{数组的替代品}

\subsubsection{模板类vector}

头文件vector

包含在命名空间std中

是一种动态数组

看下面的代码示例

\verb|vi|是一个\verb|vector<int>|对象，\verb|vd|是一个\verb|vector<double>|对象。

一般而言，使用\verb|vector<typename> vt(n_elem)|创建vector对象，存储\verb|n_elem|个类型为\verb|typename|的元素。

\begin{lstlisting}
#include <vector>

using namespace std;
vector<int> vi;
int n;
cin >> n;
vector<double> vd(n);
\end{lstlisting}

\subsubsection{模板类array}

头文件array

array对象的长度是固定的，不像vector是动态数组。

array对象使用栈(静态内存分配)，而不是自由存储区。效率与数组相同，更方便，更安全。

创建array对象的一般格式，\verb|array<typename, n_elem> arr;|，与vector的区别是这里的\verb|n_elem|不能是变量。

\subsubsection{数组、vector、array的区别}

vector和array都可以使用访问数组成员的方法来访问其成员

在存储的地址方面，数组和array存储在栈中，而vector存储在堆、或者自由存储区中。

可以将一个array对象赋值给另外一个array对象，而数组不行。

\chapter{C库函数}
\section{cstring}
\subsection{memset()}
作用是复制字符\verb|c|(一个无符号字符)到参数 \verb|str| 所指向的字符串的前 \verb|n| 个字符

头文件：C中\verb|#include<string.h>|，C++中\verb|#include<cstring>|
\begin{lstlisting}
void* memset( void* dest, int ch, std::size_t count );
\end{lstlisting}

\verb|memset()|是按字节赋值，将参数\verb|c|转换为二进制之后，填入字节。可以用于将内存块清零或设置为特定值。

\verb|memset()|并不会对\verb|void *str|做边界检查，所以要注意防止越界。

\verb|memset()| 的第二个参数通常是一个 \verb|int| 类型的值，但实际上只使用了该值的低8位。

\subsection{memcpy()}
\begin{lstlisting}
void* memcpy( void *restrict dest, const void *restrict src, size_t count );  // since C99
\end{lstlisting}

从存储区 \verb|src| 复制 \verb|count| 个字节到存储区 \verb|dest|。

该函数返回一个指向目标存储区 \verb|dest| 的指针。

与\verb|strcpy(), strcpy_s()|类似，\verb|memcpy()|也有对应的\verb|memcpy_s()|版本。
\begin{lstlisting}
errno_t memcpy_s( void *restrict dest, rsize_t destsz, const void *restrict src, rsize_t count );
\end{lstlisting}

\section{cstdio}
\subsection{fgets()}
从指定的流 stream 读取一行，并把它存储在 str 所指向的字符串内。当读取 (n-1) 个字符时，或者读取到换行符时，或者到达文件末尾时，它会停止，具体视情况而定。
\begin{lstlisting}
char *fgets(char *str, int n, FILE *stream)
\end{lstlisting}

\chapter{运算符}
C++运算符主要包含以下几类
\begin{itemize}
\item 算术运算符
\item 关系运算符
\item 逻辑运算符
\item 位运算符
\item 赋值运算符
\item 其他
\end{itemize}

\section{算术运算符}
\subsubsection{递增递减运算符}
\subsubsection{基本用法}
递增运算符\verb|++|、递减运算符\verb|--|

放在操作数的前面和后面，具有不同的效果

后缀时，\verb|a++|，指的是使用\verb|a|的当前值来计算表达式，然后再将\verb|a|的值增加1

前缀时，\verb|++a|，指的是先将\verb|a|加1，再来计算表达式。

\verb|a++|和\verb|++a|，在某些编译器和设备上，可能会有不同的表现。在此不多赘述。

\subsubsection{副作用、顺序点}

副作用(side effect)，指的是在计算表达式时，对某些东西进行了修改(比如存储在变量中的值)

顺序点(sequence point)，指的是程序执行过程中的一个点。

我们认为，在编程时，进入下一个顺序点之前，应当对所有发生的副作用要进行评估。

在C++中，语句中所有的分号就是一个顺序点。

任何完整的表达式的末尾都是一个顺序点。

例如，\verb|4 + x++|就不是一个完整表达式，没有分号，所以执行完之后，\verb|x|并不会加1。

\verb|y = (4 + x++) + (6 - x++);|，这条语句执行完之后，\verb|x|会加2。建议是避免在实际编程中使用这种表达式。

\subsubsection{递增递减运算符与指针}

可以将递增运算符用于指针和基本变量。本书前面介绍过，将递增运算符用于指针时，将把指针的值增加其指向的数据类型占用的字节数，这种规则适用于对指针递增和递减

看如下的代码示例

\begin{lstlisting}
double arr[5]= {21.1, 32.8, 23.4, 45.2, 37.4};
double *pt = arr;  // pt指向arr[0]
++pt;  // 此时，pt指向arr[1]
\end{lstlisting}

\subsubsection{优先级的问题}

前缀递增、前缀递减、解除引用运算符(\verb|*|)的优先级是相同的，将以从右至左的顺序结合。

后缀递增、后缀递减的优先级相同，比前缀的优先级高。

\verb|*++pt|的含义是，\verb|pt|先加1，然后解除引用运算符(\verb|*|)起作用。

\verb|++*pt|的含义是，先取得指针\verb|pt|的值，再将这个值加1。

\verb|*pt++|，后缀递增的优先级高于解除引用运算符(\verb|*|)，但是后缀递增的规则又是，先运算表达式，再加1。因此\verb|*pt++|的值为\verb|arr[2]|，该语句执行完毕后，\verb|pt|的值将为\verb|arr[3]|的地址。

\section{其他}

\subsubsection{条件运算符}
三元条件运算符(ternary conditional)

条件运算符指的是问号\verb|?|，他的使用格式是

\begin{lstlisting}
Exp1 ? Exp2 : Exp3;
\end{lstlisting}

如果\verb|Exp1|为\verb|true|则返回\verb|Exp2|的结果，否则返回\verb|Exp3|

条件运算符可以用来代替如下所示的if-else语句

\begin{lstlisting}
if(condition) {
    var = X;
} else {
    var = Y;
}
\end{lstlisting}


\chapter{循环、判断、逻辑运算}

\section{循环}

\subsubsection{for循环}

\begin{lstlisting}
for (initialization; test-expression; update-expression)
    statements;
\end{lstlisting}

\verb|test-expression|部分的作用是进行循环判断，若为1，则继续循环，为0，则退出循环

C++会强制将其结果转换为\verb|bool|类型

\verb|update-expression|在每次循环体执行完之后执行1次

\verb|for|和后面括号之间一般加上一个空格，(而函数名与括号之间一般没有空格)

递增递减运算符，详见运算符那部分。

\subsubsection{while循环}

\begin{lstlisting}
while (test-condition)
    statements;
\end{lstlisting}

\subsubsection{do-while循环}

\begin{lstlisting}
do
    statements;
while (test-expression);
\end{lstlisting}

先执行循环体，再判断，如果结果为0，则退出循环。

\verb|while|后面要有分号

\subsubsection{基于范围(range-based)的for循环(C++11)}

详见第\ref{基于范围的for循环}节

\section{循环与文本I/O}

看下面的代码示例

省略了空格，因为\verb|cin|会跳过空格和换行符等。

发送给\verb|cin|的输入被缓冲，只有在用户按下回车键后，输入的内容才会被发送给程序。这就是在运行该程序时，可以在\#后面输入字符的原因。按下回车键后，整个字符序列将被发送给程序，但程序在遇到\#字符后将结束对输入的处理。

\begin{lstlisting}
int main()
{
    using namespace std;
    char ch;
    int count = 0;
    cout << "Enter characters; enter # to quit:\n";
    cin >> ch;
    while (ch != '#')
    {
        cout << ch;
        ++count;
        cin >> ch;
    }
    
    cout << endl << count << " characters read\n";
    return 0;
}
\end{lstlisting}

那么如何优化该程序呢？想让他能够逐个字符地处理代码，而不是一股脑地输入给\verb|cin|，然后\verb|cin|还要挑挑摘摘，把里面的空格和换行符删掉。

看下面的代码示例。

该程序会逐个处理字符了，而不会把空格和换行符忽略掉。但是仍然会有缓冲存在，输入的字符比最终到达程序的要多。

这里使用\verb|cin.get(ch)|，传递的是\verb|ch|而不是\verb|&ch|。头文件\verb|iostream|将\verb|cin.get(ch)|的参数声明为引用类型，因此该函数可以修改其参数的值。

\begin{lstlisting}
int main ()
{
    using namespace std;
    char ch;
    int count = 0;
	
    cout c<"Enter characters; enter # to quit : \n";
	
    cin.get(ch);
    while(ch != '#')
    {
        cout << ch;
        ++count;
        cin.get(ch);
    }

    cout << endl << count << " characters read\n";
    return 0;
}
\end{lstlisting}

\subsubsection{文件尾EOF}

文件尾EOF的概念与重定向的用法有联系。

键盘模拟文件尾，比如windows下使用ctrl+Z或者回车

键盘输入文件尾EOF，是命令行操作系统的一种遗留。

检测到EOF后，cin将两位(eofbit和 failbit)都设置为1。可以通过成员函数eof()来查看eofbit是否被设置；如果检测到EOF，则cin.eof()将返回bool值true，否则返回false。

同样，如果eofbit或failbit被设置为1，则fail()成员函数返回true，否则返回false。

注意，eof()和fail()方法报告最近读取的结果；也就是说，它们在事后报告，而不是预先报告。因此应将cin.eof()或cin.fail()测试放在读取后。

每次读取一个字符，直到遇到EOF的输入循环的基本代码设计，可以看下面的程序示例

\verb|while|的判断条件，更有甚者可以写成\verb|while(cin)|的形式，这种写法有一个优点是，可以识别出其他故障，比如磁盘读写故障等。

\begin{lstlisting}
cin.get(ch);

while (cin.fail() == false)  // 也可以写作while (!cin.fail())
{
    ...
    cin.get(ch);
}
\end{lstlisting}

EOF不表示输入中的字符，而是指出没有字符。

EOF表示的不是有效字符编码，因此可能不与char类型兼容。例如，在有些系统中，char类型是没有符号的，因此char变量不可能为EOF值，即-1。



\section{(incomplete)判断}

\section{(incomplete)逻辑运算}

\part{函数与对象}
\chapter{函数}
\label{函数}

\section{函数基本知识}

无返回值的函数

\begin{lstlisting}
void functionName(parameterList)
{
	statement(s);
	return;
}
\end{lstlisting}

有返回值的函数

返回值的类型必须是\verb|typename|类型，或者可以是可以被强制转换为\verb|typename|类型的类型

\begin{lstlisting}
typename functionName(parameterList)
{
	statement(s);
	return value;
}
\end{lstlisting}

函数原型(prototype)描述了函数到编译器的接口。

加分号是函数原型，不加分号是函数头(function head)。

使用函数前先声明。

函数原型中的变量列表不需要有变量名，只需要写明类型即可。

\begin{lstlisting}
void say_hi();  // C的写法
void say_hi(parameterList);  // C++的写法
\end{lstlisting}

C++中认为空括号就相当于\verb|void|，相当于该函数没有参数。

\section{函数参数、参数列表、按值传递、默认参数}

形参parameter，实参argument

参数列表，又称为函数的特征标

参数列表中，两个参数的类型相同，也要分开写，不能像声明变量那样合并着写。

宏不能按值传递

\section{向函数输入数组}

\subsubsection{数组名的含义}

在C++中，当且仅当用于函数头或函数原型时，\verb|int *arr|和\verb|int arr[]|的含义是相同的，都表示\verb|arr|是一个指向\verb|int|类型数据的指针。

C++将数组名解释为第一个元素的地址。

\begin{lstlisting}
arr[i] == *(arr + i);
&arr[i] == arr + i;
\end{lstlisting}

无论\verb|arr|是"指向数组第一个元素的指针"，还是"数组名"，\verb|arr[3]|都表示数组的第4个元素。

\subsection{方法1：数组名与数组长度}

一般来说，第一个形参是数组名，而第二个形参给出数组长度。

数组名的具体含义，在上文中已经讲过了。

为什么要给出数组长度？由于只传递了一个指针，长度要额外的形参来接收，所以必须显式地告诉函数。

传递指向数组的元素的指针时，可以用\verb|cookies+i|的形式，也可以\verb|&cookies[i]|

当数组作形参时，实际上，是将实参(即数组名)，即数组的地址赋给了形参，即传给函数一个指针。

说明，我们函数操作的是原来的数组。

\begin{enumerate}
\item 传递常规变量时，函数会将值赋值给形参。
\item 传递数组时，函数使用原来的数组，形参收到的只是一个指针。
\end{enumerate}

为了防止函数无意中修改数组内容，在声明时可以对形参使用关键字\verb|const|来修饰，比如\verb|void show_array(const double array[], int n);|

\subsubsection{将const关键字用于指针}

\begin{enumerate}
\item 将常规变量的地址赋给常规指针。最基本的情况，不会发生“无意中修改数组内容”的情况。
\item 将常规变量的地址赋值\verb|const|指针。

\begin{lstlisting}
int age = 39;
const int * pt = &age;
\end{lstlisting}

\verb|*pt|是不能修改的，修改只能修改\verb|age|的值
\item 将\verb|const|变量的地址赋给常规指针，这种情况不可行。
\item 将\verb|const|变量的地址赋给\verb|const|指针，可行。
\end{enumerate}

\subsection{方法2：通过区间来输入数组}

给定两个指针，一个指向数组第一个元素，一个指向数组最后一个元素。

距离，对于\verb|double arr[20]|，则指针\verb|arr|和\verb|arr+20|就定义了这样的一个区间

\subsection{总结}

需要处理数组的函数，一般具有如下格式

\begin{lstlisting}
functionName(double arr[], int n);  // 可以修改数组内容
functionName(const double arr[], int n);  // 不修改数组内容
\end{lstlisting}

\subsection{函数和二维数组}

\begin{lstlisting}
int data[3][4];
int total = sum(data, 3);

int sum(int (*ar2)[4], int size);
// 或者写成下面的形式
int sum(int ar2[][4], int size);
\end{lstlisting}

在上面的代码中，

data表示一个指针，指向一个数组，数组里有4个int值

\verb|(*ar2)|的圆括号是必不可少的。

\begin{enumerate}
\item \verb|int *ar2[4]|表示一个数组，数组里面有4个指向int的指针
\item \verb|int (*ar2)[4]|表示一个指针，指向一个数组，数组里面有4个int的值
\end{enumerate}

这两种写法都表示ar2是指针，而不是数组

而且也规定了，它指向由4个int值组成的数组

即，指针类型就规定了列数。

\verb|sum()|函数只能接收4个int组成的数组，即，列数被隐式地传递给函数了。

ar2是指向由4个int组成的数组的指针，可以看做是一个二维数组的数组名。

\begin{lstlisting}
int sum(int ar2[][4], int size)
{
	int total = 0;
	for ( int r = 0; r < size; r++)
		for ( int c = 0; c < 4; c++)
			total += ar2[r][c];
	return total;
}
\end{lstlisting}

\verb|ar2 + r|指向编号为r的元素，即\verb|ar2[r]|

\verb|ar2[r]|本身是数组名，给它加上上下标就能得到数组里面的元素，\verb|ar2[r][c]|

\verb|ar2[r][c] == *(*(ar2 + r) + c)|

\section{向函数输入结构}

\subsubsection{数组名和结构名的区别}

按值传递结构时，函数会使用原结构的副本

数组名和结构名的区别

\begin{enumerate}
	\item 数组名就是数组第一个元素的地址
	\item 结构名只是结构的名，是一个标识符，并不是地址，获取结构的地址还需要用取址运算符\verb|&|
\end{enumerate}

\subsubsection{三种方法}

将结构作为函数的输入，有三种方法

\begin{enumerate}
	\item 传递整个结构，但是会占用大量内存。
	\item 传递结构的地址，使用指针来访问结构的内容。
	\item "按照引用传递"，是C++提供的新方法。
\end{enumerate}'

\subsubsection{方法：向函数传递结构的地址}

\begin{enumerate}
	\item 实参似乎结构的地址(例如\verb|&pplace|)，而不是结构本身(例如\verb|pplace|)
	\item 形参是结构类型的指针，并用const关键字修饰
\end{enumerate}

\section{向函数输入对象}

可以按值传递，把对象传递给函数，这样函数可以操作对象的副本，并不会对原对象本身做任何改动。

也可以传递给对象的指针，这样可以让函数操作原始对象。

也可以操作对象的引用。

\section{递归}

C++函数有一种有趣的特点一一可以调用自己，这种功能被称为递归。

与C语言不同的是，C++不允许\verb|main()|调用自己

尽管递归在特定的编程(例如人工智能)中是种重要的工具

如果递归函数调用自己，则被调用的函数也将调用自己，这将无限循环下去，除非代码中包含终止调用链的内容。通常的方法将递归调用放在if语句中

\subsection{包含一个递归调用的递归}

一个典型的递归函数的格式，可以看下面的代码示例

\begin{lstlisting}
void recurs(argumentlist)
{
    statements1
    if (test)
        recurs(arguments)
    statements2
}
\end{lstlisting}

只要每个if判断的结果为true，则就会再执行一遍\verb|recur()|

每次执行\verb|recur()|时，会先执行statements1，而暂时不会执行statements2

\begin{lstlisting}
#include <iostream>
void countdown(int n);

int main ()
{
    countdown(4);
    return 0;
}
    
void countdown (int n)
{
    using namespace std;
    cout << "Counting down ... " << n << endl;
    if (n > 0)
        countdown(n-1);
    cout << n << ": Kaboom ! \n";
}
\end{lstlisting}

\subsection{包含多个递归调用的递归}

在需要将一项工作不断分为两项比较小的、类似的工作的时候，递归非常有用。

考虑这样一种需求，绘制标尺。首先标出两端，找到中点并将其标出。

然后将同样的操作用于绘制出的左半部分和右半部分。

如果要进一步细分，可以将同样的操作继续用于生成出的小部分上，划分出更小的部分。

\section{指向函数的指针}

\subsection{什么是函数指针}

\subsubsection{获取函数指针}

函数的地址，是其机器语言储存在内存中开始的地址。

函数名，即表示函数的地址(和数组类似)

\paragraph{举例}

例如，对于某函数\verb|think()|来说，\verb|think|就是该函数的地址。

\verb|process(think)|，将\verb|think()|函数的指针，传递给\verb|process()|函数。

这样，在\verb|process()|函数内部可以调用\verb|think()|函数了。

\paragraph{注意区分}

\verb|process(think)|和\verb|process(think())|之间的区别。

\begin{itemize}
    \item \verb|process(think)|表示向\verb|process()|传递\verb|think()|函数的地址
    \item \verb|process(think())|表示向\verb|process()|传递\verb|think()|函数的返回值
\end{itemize}

\subsubsection{声明函数指针}

\paragraph{格式} 声明函数指针的格式，形如下面的形式。

\verb|double (*funcPointName)(int);|

注：这里\verb|(*funcPointName)|必须加上圆括号，主要原因来自于运算符的优先级关系，圆括号的运算级比\verb|*|高。

思路：如果\verb|double funcName(int);|是函数，则\verb|double (*funcPointerName)(int);|也是函数，则\verb|funcPointerName|就是函数指针。

\paragraph{注意区分}

\begin{itemize}
    \item \verb|*funcPointName(int)|表示，\verb|funcPointName()|是一个返回指针的函数。这里可能没写完，星号\verb|*|前面还会有一个其他类型的类型名。其完整的函数表达式，也许是类似\verb|double *funcPointName(int)|这样的。
    \item \verb|(*funcPointName)(int)|表示，\verb|funcPointName|是一个指向函数的指针，而\verb|funcPointName()|是函数
\end{itemize}

\subsubsection{给函数指针赋值}

正确声明了一个函数指针后，就可以将函数的地址赋值给他。

\begin{lstlisting}
double pam(int);
double (*pf)(int);  // 这里的double和int必须对应相同
pf = pam;
\end{lstlisting}

\subsection{使用函数指针}

\verb|(*pf)(5)|就相当于\verb|pam(5)|

\subsubsection{函数指针与函数式编程}

这部分需要了解函数式编程之后才可以阅读

\verb|void call_twice(void func()){ func();func(); };|

这个函数的功能，就是将传入的函数，调用两遍。传入的其实是这个函数的起始地址。

调用时的写法是\verb|call_twice(print_price);|

这里不能写成\verb|print_price()|，因为这样写就成了表达式，而\verb|print_price|才是函数的起始地址。

\subsection{关键字typedef}

关键字\verb|typedef|可以创建数据类型的别名。

\verb|typedef double real;|

\begin{lstlisting}
const double * f1(const double ar[], int n);
const double * f2(const double [], int);
const double * f3(const double *, int);
\end{lstlisting}

这三种写法的作用是一样的。

首先，\verb|const double ar[]|和\verb|const double *ar|的含义完全相同，具体原因请回想数组名的含义，即都表示ar是一个指向double类型的数组的指针。

又因为，在形参列表里，可以省略标识符，只保留数据类型，所以就得到了第三种写法。

声明一个指向函数的指针，指针名为\verb|pa|

\verb|const double * (*pa)(const double [], int);|

可以在后面加上\verb|= f1|进行初始化

声明一个包含3个指向函数的指针的数组

\verb|const double * (*pa[3])(const double [], int) = {f1, f2, f3};|

\section{内联函数}

\subsection{内联函数的作用}

\subsubsection{为什么设计内联函数这一功能}

函数的引入可以减少程序的代码量，使得函数程序可以在代码间共享。但函数调用需要一些时空开销。在调用函数时，要先中断调用函数，将执行流程转移到被调函数中，待被调函数执行完毕后，返回调用函数。

在编译时，编译器将程序中的\verb|inline|函数调用都用其函数体来代替，执行的计算完全相同。虽然目标程序的代码量会增加，但节省了非内联函数调用时的栈内存的创建和释放开销，这是一个以空间文件大小来换时间的方法，提高运行效率和性能。

对于较长的函数，这种开销可以忽略不计。但对于很短的、调用频繁的函数体，这种开销不能忽略。

\subsubsection{常规函数}

使程序跳到函数的地址，函数结束后返回，将函数参数复制到堆栈。

\subsubsection{内联函数}

不进行跳转，转而直接执行函数的代码，运行速度更快，但是占用更多内存。

如果一个函数是内联的，那么在编译时，编译器会把该函数的代码副本放置在每个调用该函数的地方。

如果程序在10个地方调用同一个内联函数，则最终程序会包含该代码的10个副本。

内联函数是C++的新特性

C语言中，类似的功能是通过预编译\verb|#define|语句来实现的

\subsection{内联函数的用法}

\subsubsection{声明内联函数}

在函数声明和函数定义之前加上关键字\verb|inline|

\subsubsection{使用内联函数的一些注意事项}

\begin{enumerate}
	\item 内联函数是不能递归的
	\item 如果语句较多，不适合定义为内联函数
	\item \verb|inline|函数体中，不能有循环语句，\verb|if|语句或者\verb|switch|语句，否则，即使函数定义时使用了\verb|inline|关键字，编译器也会将其当成普通函数来处理
	\item \verb|inline|函数应在声明和调用前进行定义，否则，有的编译器会将其当成普通函数，无法提高程序的运行效率，而有的编译器可能会报错
\end{enumerate}



\section{引用变量}

\subsection{什么是引用变量}

是一种复合类型

引用，是已经定义的变量的另一个名称

引用变量的主要作用是用作函数的形参

\verb|&|除了表示变量的地址，也可以用来声明引用

引用必须在声明引用的时候就进行赋值，不能分开。

举例

\begin{lstlisting}
int rats;
int & rodents = rats;  // rodents是rats的一个引用
\end{lstlisting}

rats和rodents的值和地址都相同

\verb|int * p_rats = & rats;|这里的\verb|p_rats|是指针

\verb|int * const pr = &rats;|等价于\verb|int & rodents = rats;|

说明，引用变量，更像是\verb|const|指针。

引用也可以使用\verb|const|关键字，不会修改原来的值，例如\verb|double refcube(const double &ra);|

\subsection{将引用作为函数参数}

即，按引用传递，与按值传递是不同的。

除了指针以外，引用也为函数处理大型结构提供了便利。

C语言只能够按值传递，或者按指针传递。按引用传递是C++独有的特性。

向函数传递一个引用时，函数可以使用原始数据、对原始数据进行操作。

\subsubsection{按值传递与按引用传递的区别}

使用简单数据类型的函数，尽量使用按值传递。

当涉及比较大的数据结构时(比如结构和类)，使用按引用传递更方便。

如果想使用引用，又不想修改其原本的值，则可以使用常量引用，即前面加上关键字\verb|const|。

例如\verb|double refcube(const double &ra);|(原书代码示例中的函数)

传递引用的限制更严格，比如\verb|double z = refcube(x + 3.0);|这样的调用是不合法的。简单来说，原因是\verb|x + 3.0|不是\verb|double&|类型

\subsection{临时变量的引用 左值(left-value)}

如果实参与引用变量不匹配，C++将生成临时变量。

但是\verb|x + 3.0|是无法转换为\verb|double&|类型的。(如果说\verb|ra|，也即\verb|double&|，指的是变量的一个别名，这也是引用的一种定义，则实参，本质上应该是一个变量。他是变量的别名，他自然也是一个变量。但是明显\verb|x + 3.0|并不是一个变量variable，这是一个表达式expression)

下面就要引出\textbf{左值}的概念。

\subsubsection{什么是左值(left value)}

\textbf{左值(left value)}：可以建立引用的对象

\textbf{非左值}，无法建立引用的对象，包括字面常量，包含由多个项组成的表达式等。

在C语言中，左值最初指的是可以出现在赋值语句左边的实体。常规变量和\verb|const|变量都是左值，但是区别在于常规变量是可以修改的，\verb|const|变量是不能修改的。

引用和解除引用的指针，也都是左值。

如果实参和引用参数不匹配，C++将生成临时变量。

对于下文的这样一个函数

\begin{lstlisting}
double refcube(const double & ra)
{
    return ra * ra * ra;
}
\end{lstlisting}

像下文这样的函数调用，虽然实参类型不正确，但是可以转换成正确的类型。编译器将创建一个临时变量，使它初始化为7.0或者\verb|side + 10.0|，并将之传递给\verb|refcube()|，函数调用结束后，该临时变量便被销毁。

\verb|double c6 = refcube(7.0);|

\verb|double c7 = refcube(side + 10.0);|

\begin{itemize}
\item 如果对于某个接收引用的函数，他的作用是修改他所接受到的变量本身，那么，创建临时变量这个做法，就与该函数的目的是相悖的，因为函数的一切操作都会作用于一个临时变量上，而非传入的变量其本身。C++的解决办法是，禁止创建临时变量。
\item 而对于函数\verb|double refcube(const double & ra)|来说，该函数并非要修改传入的变量的值，而只是使用这个变量的值而已。因此，如果将引用声明为\verb|const|，C++编译器则会在需要时生成临时变量。
\end{itemize}

尽可能的使用\verb|const|引用

将引用参数声明为\verb|const|引用的理由：

\begin{enumerate}
\item 使用\verb|const|可以避免发生无意中修改数据的情况。
\item 使用\verb|const|使得函数可以处理\verb|const|和非\verb|const|实参，否则将只能接收非\verb|const|实参。
\item 使用\verb|const|引用使得函数能够正确生成、正确使用临时变量。
\end{enumerate}

\subsubsection{右值引用(right value reference)}

C++中新增了\textbf{右值引用(right value reference)}

这种引用可以指向右值，使用\verb|&&|声明。

看一下代码示例

\begin{lstlisting}
double && rref = std::sqrt(36.00);
double j = 15.0;
double && jref = 2.0 * j + 18.5;
std::cout << rref << '\n';  // display 6.0
std::cout << jref << '\n';  // display 48.5
\end{lstlisting}

右值引用可以用来实现移动语义(move semantics)

之前的引用，为了区分，可以成为\textbf{左值引用}。

\subsection{将引用用于结构}

\verb|structName & referName|表示指向某一个结构变量的引用

看下面的代码示例

\begin{lstlisting}
struct free_throws
{
    std::string name;

    int made;
    int attempts;

    float percent;
}
\end{lstlisting}

则可以这样编写函数原型，在函数中将指向该结构的引用作为参数

\verb|void set_pc(free_throws & ft);|

如果不希望函数修改传入的结构，可使用\verb|const|

\verb|void display(const free_throws & ft);|

看下面的代码示例

\verb|display()|按引用传递，不修改本身的值，所以在函数定义里加了\verb|const|关键词

\verb|display(accumulate(team,two));|，\verb|accumulate()|返回一个\verb|free_throws &|，又传递给了\verb|display()|

\begin{lstlisting}
// 函数声明
void display(const free_throws & ft);
void set_pc(free_throws & ft);
free_throws & accumulate(free_throws & target, const free_throws & source);

// 变量声明
free_throws one = { "Ifelsa Branch", 13, 14};
free_throws two = { "Andor Knott", 10, 16};
free_throws three = { "Minnie Max" ,7, 9};
free_throws four = { "whily Looper",5, 9};
free_throws five = { "Long Long" ,6, 14};
free_throws team = { "Throwgoods" , 0, 0};

// 函数调用
set_pc (one);
display(one);
display(accumulate(team,two));
\end{lstlisting}

\subsubsection{返回引用和返回值的区别}

跟按值传递和按引用传递的区别类似。

传统的返回值，使用\verb|return|语句，返回的数据会先放到一个临时变量里，然后再传递给下一个函数使用。

传统的返回方式，有时会临时占用一大块内存。

而返回引用，则会节省出这一块临时占用的内存。用了某一块内存，即可，不会再临时占用其他内存了。

\subsubsection{返回引用时的注意事项}

避免返回，在函数中止时内存单元会被清除的引用，比如临时变量。

比如下面的代码示例，是应当避免的情况。函数返回一个临时变量\verb|newguy|，函数调用结束之后就消失了，这时候，这个引用本身也就无效了。

\begin{lstlisting}
const free_throws & clone2(free_throws & ft)
{
    free_throws newguy;
    newguy = ft;
    return newguy;
}
\end{lstlisting}

避免无意义的指针。

可以返回传递给函数的引用，正如上文的\verb|accumulate()|所使用的那样，或者使用\verb|new|运算符

\section{函数重载}

\subsection{什么是函数重载}

\subsubsection{多态(polymorphism)}

函数多态(polymorphism)是C++在C语言的基础上新增的功能。

术语"多态"指的是有多种形式，因此函数多态允许函数可以有多种形式。类似地，术语"函数重载"指的是可以有多个同名的函数，因此对名称进行了重载。这两个术语指的是同一回事。

通过函数重载来设计一系列函数，它们完成相同的工作，但使用不同的参数列表。

\subsubsection{函数重载(overload)}

在函数定义时使用默认参数，可以让您能够使用不同数目的参数调用同一个函数，而函数重载(overload)可以让您能够使用多个同名的函数。

仅仅有函数返回变量的类型不同，这时不算函数重载。

例如，同时存在下面的这两种函数声明，是不允许的。

\begin{lstlisting}
long great(int n, float m);
double great(int n, float m);
\end{lstlisting}

\subsubsection{函数特征标(function signature)}

\begin{enumerate}
\item 函数的参数列表，又称为函数特征标(function signature)。函数的返回类型，并不在函数特征标的考虑范围内。

\item 如果两个函数的参数数量和类型相同，而且其排列顺序也相同，那么称这两个函数的函数特征标就相同。

变量名是无关紧要的，重要的是类型，数量和顺序。

\item 函数的多态，指的是函数名相同，但是函数特征标不相同。

\item 编译器在检查函数特征标时，把对于某类型的引用和该类型本身，看做同一个特征标。

\item 不区分\verb|const|变量和非\verb|const|变量

\item 仅仅只有返回变量的类型不同，这时不算函数重载。
\end{enumerate}

\subsubsection{名称修饰(name decoration)}

C++给每个重载函数，秘密地指定了一个身份，以便于区分它们。

也就是说，只是在程序员层面，这些函数看上去他们的函数名称是一样的。但是在编译器看来，他们都是不同的函数。

这个过程称为\textbf{名称修饰(name decoration)}或者\textbf{名称矫正(name mangling)}

\subsection{重载引用参数}

类设计和STL经常使用引用参数，因此知道不同引用类型的重载很有用。请看下面的代码示例。

\begin{lstlisting}
void sink(double & r1);         // matches modifiable lvalue
void sank(const double& r2);    // matches modifiable or const lvalue, rvalue
void sunk(double && r3);        // matches rvalue
\end{lstlisting}

左值引用参数\verb|double & r1|与可修改的左值参数(如double变量)匹配

\verb|const|左值引用参数\verb|const double& r2|与可修改的左值参数、\verb|const|左值参数和右值参数(如两个double值的和)匹配

右值引用参数\verb|double && r3|与右值匹配。

如果没有定义\verb|void sunk(double && r3);|，则\verb|sunk(x+y)|这样的右值引用，将与\verb|void sank(const double& r2);|匹配。(这也是上面的代码示例中，为什么有两个函数可以匹配右值引用。)

\section{函数模板}

\subsection{什么是函数模板}

让函数的参数列表是空缺的，根据需要而快速生成所需要的函数。

\subsubsection{基本语法}

\begin{lstlisting}
template <typename AnyType>
void functionName(AnyType &a, AnyType &b)
{
    body
}
\end{lstlisting}

在上面的代码中，\verb|template|是用于函数模板的关键字。

在标准C++98添加关键字\verb|typename|之前，C++使用关键字\verb|class|来创建模板。在旧代码中可以看到这种写法。

\subsubsection{模板的命名}

\verb|AnyType|是模板的命名。

模板的命名，这里可以使用很简介的标识符，比如直接用一个字母，比如\verb|T|

在使用该函数时，根据输入的变量的类型，来运行函数。写上去都写成\verb|functionName(m, n)|。

其中m和n是\verb|int|类型的。AnyType就看做是\verb|int|。

m和n是\verb|double|的，那么AnyType就看做是\verb|double|。

\subsubsection{模板在代码中的位置\enspace 声明的顺序}

在主函数之前，除了要声明的函数原型之外，也要声明函数模板。

最终的机器码里，并不包含函数模板，而是生成了的具体函数。

模板的声明和实现不能分离，因为编译器对模板的编译是惰性的，只有当前\verb|*.cpp|文件里用到了这个模板，该模板里的函数才会被定义(被生成，否则是不会有函数的机器码产生的)

\subsubsection{关键字typename}

在上文的函数模板的示例代码中，可以看到关键字\verb|typename|

在这种应用场合下，这里既可以用\verb|typename|也可以用\verb|class|

关于这个问题，Stan Lippman 曾在其博客中表示，最早 Stroustrup 使用\verb|class|来声明模板参数列表中的类型是为了避免增加不必要的关键字；后来委员会认为这样混用可能造成概念上的混淆才加上了\verb|typename|关键字

\paragraph{cppreference.com参考资料}

cppreference网站上关于关键字\verb|typename|的解释(写于2023.08.21)

\begin{enumerate}
\item 在声明一个模板的时候，在其模板参数列表中，关键字\verb|typename|在声明模板参数类型或双重模板参数类型时，可以与\verb|class|起到相同的作用。In the template parameter list of a template declaration, typename can be used as an alternative to class to declare type template parameters and template template parameters (C++17).
\item 在模板的声明和定义当中，\verb|typename|用于声明某个待决的有限定名是类型。Inside a declaration or a definition of a template, typename can be used to declare that a dependent qualified name is a type.
\item 在模板的声明或定义内， (C++11 前)typename 可在非待决的有限定类型名之前使用。此情况下它没有效果。Inside a declaration or a definition of a template, (until C++11) typename can be used before a non-dependent qualified type name. It has no effect in this case.
\item 在类型要求的要求中。(C++20 起)。Inside a requirements for type requirements (C++20) 
\end{enumerate}

\subsubsection{整数作为模板参数及编译期优化}

\verb|template<int N> void func();|和\verb|void func(int N);|有什么区别

\verb|template<int N> void func();|传入的N，是一个编译器常量，每个不同的N，编译器都会生成一份单独的代码，以便对其进行单独优化。

\verb|void func(int N);|传入的N，则是运行期常量。

比如\verb|show_times<0>();|，编译器则可以生成一个空函数，以节约资源。如果\verb|show_times<1>();|里面有一个循环体，使用N表示循环总步数，但是这个时候总步数为1，编译器则可能会删去这个循环结构，只运行一边循环体里面的内容。

这样做的缺点是，会造成编译期的时间变多。这是”编译期优化”的一个实践案例。

如果函数中有一个bool型变量，来决定函数的运行结果(例如有一个判断分支)，则可以将这个bool型变量放在函数模板中，通过编译器优化，来让编译器分别为true和false两种情况生成不同的函数，这样节省了每次运行时都会判断一次该bool型变量的时间。虽然增加了编译期时间，但是节约了运行的时间。

\subsubsection{(C++17)关键字constexpr}
如果编译期由于版本低等问题，并没有将模板参数当作编译期常量，则它不会进行优化。则可以使用C++17提供的\verb|constexpr|关键字来保证它是编译期求值，写成例如\verb|if constexpr (debug)|的形式
\verb|constexpr|里面的东西只能是编译器常量或者由编译器常量组成的表达式。

在调用时，可以通过例如\verb|constexpr bool debug = true;|的形式来将一个变量声明为编译期常量。

\verb|constexpr|关键字强迫编译器在编译期求值

模板的声明和实现不能分离，因为编译器对模板的编译是惰性的，只有当前\verb|*.cpp|文件里用到了这个模板，该模板里的函数才会被定义

\subsection{模板重载}

模板的函数特征标(参数列表)不同

并非所有的模板参数类型都是可变的，也可以有固定的情况。

写两个同名的函数模板，但是函数的定义或参数列表略有不同，这种功能就称之为模板重载。

\subsection{函数模板的局限性}

如果T为数组，则模板函数中，形如\verb|a = b|这样的语句则会失效。

如果T为结构，则模板函数中，形如\verb|if ( a > b )|这样的语句，则会失效。

总之，编写的模板函数，可能会有他们处理不了的类型。这就是模板的局限性。

解决办法

\begin{itemize}
\item 重载运算符
\item 未特定的类型提供具体的模板定义，即\textbf{显式具体化(explicit specialization)}
\end{itemize}

\subsection{显式具体化}

\textbf{显式具体化(explicit specialization)}

\subsubsection{C++98标准对显式具体化的说明}

C++98对显式具体化做了如下规定

\begin{enumerate}
\item 对于给定的函数名，可以有非模板函数、模板函数和显式具体化模板函数以及它们的重载版本。
\item 显式具体化的原型和定义应该以\verb|template<>|开头，并通过名称来指出类型。
\item 具体化优先于常规模板，而非模板函数优先于具体化和常规模板。
\end{enumerate}

\subsubsection{显式具体化的写法}

非模板函数\verb|void swap(job &, job &);|

模板函数

\begin{lstlisting}
template <typename T>
void swap(T &, T &);
\end{lstlisting}

显式具体化

\begin{lstlisting}
template<> void swap<job>(job &, job&);
\end{lstlisting}

\subsubsection{优先级}

非模板函数 \verb|>| 显式具体化 \verb|>| 模板函数

\subsection{实例化和具体化}

\subsubsection{实例化(instantiaion) 隐式实例化}

函数模板不生成函数的定义

使用函数模板为一个给定的特定类型生成某一个函数时，得到的叫做\textbf{模板实例(instantiation)}。

即，模板并非函数定义，使用某一个给定类型的模板实例是函数定义。

这种实例化的过程，被称为\textbf{隐式实例化(implicit instantiation)}

\subsubsection{显式实例化(explicit instantiation)}

C++允许\textbf{显式实例化(explicit instantiation)}，即直接命令编译器创建特定的实例。

\verb|template void functionName<int>(int, int);|

这种情况下，一定会有对应的函数定义。(隐式实例化，不使用函数模板，并不会产生相应的函数定义)

\subsubsection{具体化(specialization)}

显式具体化可以使用下面两个等价的声明

\begin{lstlisting}
template <>void functionName<int>(int&, int&);
template <>void functionName(int&, int&);  // <int>是可以省略的
\end{lstlisting}

与显式实例化的区别是\verb|void|前面有个\verb|<>|

如果进行了显示具体化，则，不仅会生成对应的函数定义。

而且还不会依靠普通模板来生成，而是基于特别定义的模板来生成其函数定义。

\subsection{重载解析(overloading resolution)}

对于函数重载，函数模板，函数模板重载，编译器需要解析当前的函数具体来自于哪个版本，即这些功能有优先级顺序。

简单说明一下这个过程

\begin{enumerate}
\item 创建候选函数列表
\item 使用候选函数列表创建可行函数列表
\item 创建最佳的可行函数
\end{enumerate}

如果确定最佳的函数(C++ Primer Plus (6th edition)原书第290页)

\begin{enumerate}
\item 完全匹配，常规函数优于模板
\item 提升转换
\item 标准转换
\item 用户定义的转换
\end{enumerate}

编译器允许的"完全匹配"和"无关紧要转换"如果同时发生的话，则会带来二义性，会报warning

\subsection{双重模板参数(template template parameters)}

将模板作为模板的参数，传递给模板

\begin{lstlisting}
template<typename T>
class MyTemplateClass
{
    // ...
};

MyTemplateClass<int> x;

template<template <typename T> typename Templ>
class MyTemplateTemplateClass
{
    // ...
};

MyTemplateTemplateClass<MyTemplateClass> y;
\end{lstlisting}

\section{SFINAE机制(substitution failure is not an error)}

\subsubsection{failure与error的区别}

failure在编程语言的语言背景下，并不是一个贬义词。它与error之间的区别在于，发生error，会中断编译过程。而发生failure不一定会中断。编译器在遇到failure之后，会进行一些补救措施，如果补救成功，并不会中断编译，编译会继续。

\subsubsection{举例说明什么时候发生failure}

比如我们有个语法分析器，其中某一个规则需要匹配一个token，它可以是标识符，字面量或者是字符串。

用代码的形式写出来，它可能是如下的形式。

假如我们传入的token是一个\verb|LITERAL_STRING|，则在匹配\verb|IDENTIFIER|的时候，就发生了一次failure。

如果它既不是\verb|IDENTIFIER|，也不是\verb|LITERAL_NUMBER|，也不是\verb|LITERAL_STRING|的时候，就发生了error。

\begin{lstlisting}
switch(token) {
case IDENTIFIER:
    // do something
    break;
case LITERAL_NUMBER:
    // do something
    break;
case LITERAL_STRING:
    // do something
    break;
default:
    throw WrongToken(token);
}       
\end{lstlisting}

\subsubsection{2次替换(substitution)}

函数模板参数(Function template parameters)会发生2次替换

\begin{itemize}
\item 在模板实参推导前，对显式特化的模板实参进行替换。Before template argument deduction, explicitly specified template arguments are substituted.
\item 在模板实参推导后，对推导出的实参和从默认项获得的实参进行替换。After template argument deduction, deduced arguments and the arguments obtained from the defaults are substituted.
\end{itemize}

\subsubsection{替换发生的场景}

替换发生于如下几种场景下

\begin{enumerate}
\item 函数类型中使用的所有类型(包括返回类型和所有形参的类型)。all types used in the function type (which includes return type and the types of all parameters).
\item 各个模板形参声明中使用的所有类型。all types used in the template parameter declarations.
\item 部分特化的模板实参列表中使用的所有类型。all types used in the template argument list of a partial specialization.
\item (C++11起)函数类型中使用的所有表达式。all expressions used in the function type
\item (C++11起)各个模板形参声明中使用的所有表达式。all expressions used in a template parameter declaration
\item (C++11起)部分特化的模板实参列表中使用的所有表达式。all expressions used in the template argument list of a partial specialization
\item (C++20起)\verb|explicit|说明符中使用的所有表达式。all expressions used in the explicit specifier
\end{enumerate}

\subsubsection{替换的顺序}

替换按词法顺序进行，并在遇到失败时终止。Substitution proceeds in lexical order and stops when a failure is encountered.


\chapter{内存模型、命名空间(namespace)}

\section{单独编译}

如果头文件包含在尖括号中，如\verb|<coordin.h>|，C++编译器将在存储标准头文件的主机系统的文件系统中查找。

如果文件名包含在双引号中，如\verb|"coordin.h"|，C++编译器将首先查找当前的工作目录或源代码的目录(或其他目录，这一点与编译器有关)，如果没找到，则去标准头文件的位置查找。

因此在包含自己编写的头文件时，应该使用引号而非尖括号。

\subsubsection{头文件管理}

在同一个文件中，只能将同一个头文件包含一次。

有一个预编译的技术可以来帮助程序员管理头文件，防止重复添加，即使用\verb|#ifndef|。

看下面的代码示例，他起到的作用是，仅当以前没有使用预编译指令\verb|#define|定义名称\verb|COORDIN_H_|时，才处理\verb|#ifndef|和\verb|#endif|之间的语句。

如果编译器在同一个文件中遇到了其他包含\verb|coordin.h|的代码，编译器就知道\verb|COORDIN_H_|已经被定义了，从而跳过后面的部分，跳转到\verb|#endif|的后面。

在这里，\verb|COORDIN_H_|是程序员自己指定的，帮助自己记忆、管理各类头文件。

\begin{lstlisting}
#ifndef COORDIN_H_
...
#endif
\end{lstlisting}

\subsubsection{文件、翻译单元}

虽然我们讨论的是根据文件进行单独编泽，但为保持通用性，C++标准使用了术语"翻译单元"(translation unit)，而非"文件"；文件并不是计算机组织信息时的唯一方式。

出于简化的耳的，本笔记使用术语"文件"。

"翻译单元"是跟编译原理有关系的一个术语。

\section{存储持续性、作用域、链接性}

\subsubsection{存储持续性}

\begin{enumerate}
\item 自动存储持续性(automatic storage duration)：执行代码块时产生，结束执行后即被释放

\item 静态存储持续性(static storage duration)：使用关键字\verb|static|定义的变量

在整个程序运行过程中都存在

C++有3种存储持续性为静态的变量。

\item 动态存储持续性(dynamic storage duration)：用\verb|new|运算符分配的内存将一直存在，直到使用\verb|delete|运算符将之释放

\item (C++11)线性存储持续性(thread storage duration)：(C++)用于并行编程
\end{enumerate}

\subsubsection{作用域}

在多大范围内可见

\subsubsection{链接性(linkage)}

名称如何在不同单元间共享 

自动变量的作用于为局部，没有linkage，不能共享

静态变量的作用于，取决于定义

全局作用域是名称空间作用域的特例

C++函数的作用域，可以是整个类或整个名称空间，但不能是局部的。如果是局部的，其他地方不能调用它，那这样的函数是没有意义的。

\subsection{自动存储持续性}

只有在所定义的函数内部才能使用他们。也包括\verb|main()|

内层的代码块里定义了一个重名的变量，则内部变量会覆盖外部的变量(hide掉外层的同名变量)

关键字\verb|auto|用于自动类型推断。

\subsubsection{自动变量和栈}

新数据象征性地放在旧数据的上面

2个指针，1个指向栈底，1个指向栈顶

增加新数据，栈顶增加，栈变高

数据使用后，删除数据，栈顶减少，栈变低，数据弹出

\subsubsection{寄存器变量}

关键字\verb|register|，来自于C语言

使用CPU来存储自动变量，可以提高访问变量的速度。

但是这一用法在C++11之后已经不推荐使用了。

\subsection{静态持续变量}

有3种链接性

\begin{enumerate}
\item 外部链接性：可在其他文件中访问，在代码块外声明。

\item 内部链接性：只能在当前文件中访问，在代码块外声明，使用\verb|static|关键字

\item 无链接性：只在当前块中可以访问，在代码块内声明，使用\verb|static|关键字
\end{enumerate}

如果没有显式地初始化静态变量，编译器将他们设置为0，这种特性称为零初始化(zero-initialization)

\subsubsection{单定义规则(ODR)}

单定义规则，One Definition Rule，ODR，指的是，变量只能有一次定义

C++提供两种变量声明

\begin{enumerate}
\item 定义声明(defining declaration)，或称为定义(definition)，它给变量分配存储空间。
\item 引用声明(referencing declaration)，或称为声明(declaration)，它不给变量分配存储空间，因为这是在引用已经存在的变量。
\end{enumerate}

引用声明，使用关键字\verb|extern|，而且不进行初始化，否则，将声明为定义，会分配存储空间。

如果要在多个文件中使用外部变量，只需在一个文件中包含该变量的定义(根据单定义规则)，但在使用该变量的其他所有文件中，都必须使用关键字\verb|extern|声明它。

ODR并不意味着，不能有多个变量的名称是相同的，可以存在几个不同的变量他们的名称是相同的。因为他们可以有在不同的作用域里有各自的名称。

\subsection{静态持续性，外部链接性}

链接性为外部，作用域为整个文件

如果要在多个文件使用外部变量，只需要在一个文件中包含该变量的定义(根据单定义规则)，但在使用该变量的其他所有文件中，都必须使用关键字\verb|extern|来声明它

看下面的代码示例

\begin{lstlisting}
// file01.cpp
// 下面带括号的extern可以省略
(extern) int cats = 20;
(extern) int dogs = 22;
(extern) int fleas;

// file02.cpp
extern int cats;
extern int dogs;
// 可以使用cats和dogs，但是不能使用fleas
\end{lstlisting}

\subsubsection{域解析运算符}

域解析运算符\verb|::|

放在变量名之前，表示使用变量的全局版本

使用时，可以使用\verb|::variableName|的形式。

不要简单把它理解成成员关系

\subsubsection{全局变量和局部变量}

程序应当避免对数据进行不必要的访问，即，如果不需要某些变量，程序无法访问他是最好的做法，有助于保持数据的完整性，称为"数据隔离"。

\subsection{静态持续性，内部链接性}

关键字\verb|static|

链接性为内部的变量，只能在其所属的文件内部使用。

如果文件定义了一个静态外部变量，其名称与另一个文件中声明的常规外部变量相同，则在该文件中，静态变量将隐藏常规外部变量，把外部变量给"hide"了。可以看下面的代码示例

\begin{lstlisting}
// 在file1.cpp中
int error = 20;
// 在file2.cpp中
int error = 5;
// 会报错，因为file2.cpp中又声明了一个名为error的变量

// 在file1.cpp中
int error = 20;
// 在file2.cpp中
static int error = 5;
// 不会报错
// 在file2.cpp中，将会使用error = 5，把error = 20给隐藏掉
\end{lstlisting}

可以使用外部变量，在多文件程序的不同部分之间共享数据。

可以使用链接性为内部的静态变量，在同一个文件中的多个函数之间共享数据。(命名空间提供了另外一种共享数据的方法)

\subsection{静态持续性，无链接性}

无链接性的局部变量

创建方法是，使用\verb|static|关键字，在代码块中定义变量。

由于使用了\verb|static|关键字，因此该变量的存储方式是静态的。

虽然该变量只在该代码块中使用，但是在结束函数调用之后，变量仍然存在，值保持不变，下次调用时仍可以使用原来的值。

\subsection{说明符和限定符}

存储说明符(storage class specifier)，或称为cv限定符(cv-qualifier)

\subsubsection{存储说明符}

\begin{enumerate}
\item \verb|auto|(在C++11中不再是说明符)
\item \verb|register|
\item \verb|static|
\item \verb|extern|
\item \verb|thread_local|(C++11新增的)
\item \verb|mutable|
\end{enumerate}

\verb|auto|用于自动类型推断。

\verb|mutable|的含义将根据\verb|const|的含义来解释，他表示，就算某类、结构为\verb|const|的，其成员仍可以修改。

\subsubsection{cv限定符}

\begin{enumerate}
\item \verb|const|：内存被初始化之后，程序便不能对齐进行修改。
\item \verb|volatile|
\end{enumerate}

关键字\verb|volatile|表示，即使程序代码没有对内存单元进行修改，其值仍可能发生变化。这种情况发生在，指针指向某一地址，该地址是对外的物理接口，因此外部可以对其值直接进行修改。

同一个声明不能使用多个存储说明符。\verb|thread_local|除外，他可以与\verb|static|或者\verb|extern|连用。

\subsection{函数和存储连续性}

C++不允许在函数中定义另外一个函数。

所有函数的存储持续性为静态的。默认的链接性是外部的。

如果想只在一个文件里使用，则需要用\verb|static|关键字，函数原型和函数定义都必须加上。

和静态变量类似，在定义静态函数的文件中，静态函数将覆盖外部定义。因此即使在该文件外部定义了同名的函数，该文件仍将使用静态函数。

单定义规则也适用于非内联函数。对于每一个非内联函数，程序只包含一个定义。由于函数的链接性为外部，这说明，在多文件系统中，只能有一个文件，包含了该函数的定义。但是每个使用该函数的文件中必须包含该函数的声明。

内联函数不受单定义规则的约束。

\section{命名空间}

\subsubsection{声明区域与潜在作用域}

声明区域(declaration region)

潜在作用域(potential scope): 从声明点开始，到声明区域的结尾

潜在作用域比声明区域小，因为变量必须定义后再使用。

潜在作用域中如果存在包含同名变量的代码块，则作用域小于潜在作用域。

\subsubsection{命名空间}

一个命名空间里的标识符不会与另一个命名空间里面的同名标识符发生冲突。

声明命名空间的基本方法，如下代码示例所示

\begin{lstlisting}
namespace Jack {
	double pail;
	void fetch ();
	int pal;
	struct well { ... };
}
namespace Jill {
	double bucket (double n){ ... };
	double fetch;
	int pal;
	struct Hill { ... };
}
\end{lstlisting}

命名空间可以是全局的，也可以位于另外一个命名空间之中，但是不能位于代码块之中。

在默认情况下，在命名空间中声明的名称的链接性是外部的。

命名空间是开放的，可以把任一的名称加入到命名空间之中。

未被装饰的名称，称为未限定的名称(unqualified name)，指的是，我们没有规定他具体属于哪一个命名空间的名称。

带有命名空间的名称，称为限定了的名称(qualified name)

访问命名空间里面的名称，使用域解析运算符(\verb|::|)

\subsubsection{using声明和using编译指令}

如果想要免去每次使用名称时都要对他的命名空间进行指定，简化代码编写流程，C++提供两个机制来解决这个问题，分别是using声明和using编译

using声明使某一个特定的名称在当前可用。

例如，通过\verb|using Jack::fetch();|，就可以直接使用\verb|fetch()|，而不用写他的全程\verb|Jack::fetch()|

using编译使整个命名空间都可用。

例如，通过\verb|using namespace Jack;|，使得\verb|Jack|里面所有的变量可用。

using编译，导入命名空间的所有名称，如果与局部名称发生冲突，则局部名称覆盖命名空间里的名称。

在全局声明区域中使用using编译指令，将使该名称空间的名称全局可用。

在函数中使用using编译指令，将使其中的名称在该函数中可用，

关于using编译和using声明，它们增加了名称冲突的可能性。也就是说，如果有名称空间\verb|jack|和\verb|jill|，并在代码中使用作用域解析运算符\verb|::|，也就是说，写他们的全称，即\verb|jack::pal|和\verb|jill::pal|，则不会存在二义性

事实上，编译器不允许您同时使用上述两个using声明，因为这将导致二义性。

\subsubsection{命名空间的其他特性}

命名空间可以嵌套

命名空间之中，也可以使用using声明和using编译


\chapter{类和对象}
\section{引论}
\subsection{认识类}
类是一种将抽象转换为用户定义类型的C++工具，它将数据表示和操纵数据的方法组合成一个的包。

下面来看一个表示股票的类。

首先，必须考虑如何表示股票。可以将一股作为基本单元，定义一个表示一股股票的类。然而，这意味着需要100个对象才能表示100股，这不现实。相反，可以将某人当前持有的某种股票作为一个基本单元，数据表示中包含他持有的股票数量。一种比较现实的方法是，必须记录最初购买价格和购买日期(用于计算纳税)等内容。另外，还必须管理诸如如拆股等事件。首次定义类就考虑这么多因素有些困难，因此我们对其进行简化。具体地说，应该将可执行的操作限制为:

\begin{itemize}
    \item 获得股票
    \item 增持
    \item 卖出股票
    \item 更新股票价格
    \item 显示关于所持股票的信息
\end{itemize}

可以根据上述清单定义stock类的公有接口(如果您有兴趣，还可以添加其他特性)。为支持该接口，需要存储一些信息。我们再次进行简化。将存储下面的信息:

\begin{itemize}
    \item 公司名称
    \item 所持股票的数量
    \item 每股的价格
    \item 股票总值
\end{itemize}

接下来定义类。一般来说，类规范由两个部分组成。简单地说，类声明提供了类的蓝图，而方法定义则提供了细节

\begin{itemize}
    \item 类声明：以数据成员的方式描述数据部分，以成员函数(被称为方法)的方式描述公有接口
    \item 类方法定义：描述如何实现类成员函数
\end{itemize}

通常，C++程序员将接口(类定义)放在头文件中，并将实现(类方法的代码)放在源代码文件中。

类名一般首字母大写

\subsection{什么是接口}
接口是一个共享框架，供两个系统(如在计算机和打印机之间或者用户或计算机程序之间)交互时使用

对于类，我们说公共接口。在这里，公众( public )是使用类的程序，交互系统由类对象组成，而接口由编写类的人提供的方法组成。

接口让程序员能够编写与类对象交互的代码,从而让程序能够使用类对象。

例如，要计算string对象中包含多少个字符，您无需打开对象，而只需使用string类提供的size( )方法。类设计禁止公共用户直接访问类，但公众可以使用方法 size()。方法size( )是用户和string类对象之间的公共接口的组成部分。通常，方法getline()是istream类的公共接口的组成部分，使用cin的程序不是直接与cin对象内部交互来读取一行输入，而是使用getline( )。

如果希望更人性化，不要将使用类的程序视为公共用户，而将编写程序的人视为公共用户。

要使用某个类，必须了解其公共接口;要编写类，必须创建其公共接口。

\section{创建类与对象}
\subsection{从实例开始:股票Stock类}\label{从实例开始:股票Stock类}
上文提到的股票Stock类，下面的代码示例展示了类定义的基本形式。

关键字\verb|class|指出了这里定义的是一个类，不能使用\verb|typename|代替\verb|class|

\begin{lstlisting}
class Stock // class declaration
{
private:
	std::string company;
	long shares;
	double share_val;
	double total_val;
	void set_tot() { total_val = shares * share_val;}
public:
	void acquire (const std::string & co, long n, double pr);
	void buy (long num, double price);
	void sell(long num, double price);
	void update(double price);
	void show ();
};  // 结尾这里的分号不要忘记
\end{lstlisting}

\verb|class Stock|定义了一个名为\verb|Stock|的类，可以通过这个类名来声明属于这个类的对象

\verb|Stock sally;|

\verb|Stock jack;|

想要储存的数据，以类的数据成员的形式存在，比如\verb|company|和\verb|shares|

要执行的操作，以类的函数成员的形式存在，例如\verb|acquire()|和\verb|buy()|

成员函数的定义，可以在类的定义中即可给出，比如\verb|set_tot()|，也可以只写函数原型。

\subsection{类成员的访问权限，关键字private和public，数据隐藏}
\subsubsection{关键字private和public}
关键字\verb|private|和\verb|public|，描述了对类成员的访问控制，那些成员可以随意访问，那些成员禁止外部访问。

使用类对象的程序都可以直接访问公有部分，但只能通过公有成员函数(或友元函数)来访问对象的私有成员。

例如，要修改Stock类的shares成员，只能通过Stock的成员函数。因此，公有成员函数是程序和对象的私有成员之间的桥梁，提供了对象和程序之间的接口。

C++还提供了第三个访问控制关键字\verb|protected|，后文会介绍。

通常，数据项放在私有部分，组成类的接口的成员函数，放在公有部分。

使用私有成员函数来处理一些不属于公有接口的实现细节。

\subsubsection{类与结构的区别}
类，从目前来看，给人的感觉很像是包含成员函数和\verb|public|、\verb|private|的可见性标签的一种\verb|struct|。

类和结构的其中一个区别就是：结构的成员的默认访问类型都是public的

C++对结构的功能进行了一些丰富，但是惯例是，大家只用结构来表示纯粹的数据对象，而不带有任何其他丰富的功能，后者全部用类来实现。

\subsubsection{数据隐藏}
防止程序直接访问数据被称为数据隐藏

数据隐藏是体现出封装的一种形式，将实现的细节隐藏在私有部分之中，就如\verb|Stock|类里的私有的\verb|set_tot()|方法

另外一个体现封装的地方是，将类函数定义和类声明，放在不同的文件之中，可以对于同样的一套类声明换用不同的类函数定义，实现解耦。

数据隐藏不仅可以防止直接访问数据，还让开发者(类的用户)无需了解数据是如何被表示的。

例如，show()成员将显示某支股票的总价格(还有其他内容)，这个值可以存储在对象中(上述代码正是这样做的)，也可以在需要时通过计算得到。

从使用类的角度看，使用哪种方法没有什么区别。所需要知道的只是各种成员函数的功能；也就是说，需要知道成员函数接受什么样的参数以及返回作么类型的值。

原则是将实现细节从接口设计中分离出来。如果以后找到了更好的、实现数据表示或成员函数细节的方法可以对这些细节进行修改，而无需修改程序接口，这使程序维护起来更容易。

\subsubsection{使用类}
C++的目标是，让使用类与使用基本的内置类型(如\verb|int|、\verb|char|等)尽可能的相同。

比如下面的这些操作，对于基本的内置类型来说，通过前文的学习，已经能够实现了。但是对于类，还需要继续进行深入，了解与类有关的更高级的技巧。

\begin{enumerate}
\item 创建类对象，即声明类变量
\item 可以使用\verb|new|来分配存储空间
\item 可以将对象作为函数的参数和返回值
\item 将一个对象赋给另外一个对象
\item 让对象识别其他的对象
\item 相似的对象之间进行自动类型转换
\end{enumerate}

\subsection{成员变量的默认值}
C++11 之前对非静态数据成员初始化需要用到初始化列表。有个问题是，如果类的数据成员比较多，我们又需要定制一些数据成员的初始化操作的时候，需要写很多的构造函数。

C++11 允许在声明非静态数据成员的时候同时用\verb|=|(声明时默认初始化)和\verb|{}|(列表初始化)。

\begin{lstlisting}
class A {
public:
    int a_ = 0;
}
\end{lstlisting}

\subsubsection{(C++20)位域默认初始化}
C++20开始，如果对成员有位域默认初始化的需求，则可以按类似的方式进行，详见第\ref{(C++20)位域默认初始化}节。

\subsection{const对象}
我们已知，\verb|const|变量，要求这个变量在初始化之后是不能被改变的。

\verb|const|对象与之类似，但是含义更多了一层。即，\verb|const|对象的要求是，对象内部的所有数据成员都不可被改变，也不允许调用这个对象的非\verb|const|方法成员函数。

定义一个\verb|const|对象时，关键字\verb|const|和类名的前后顺序，并不固定，怎么写都可以。

\verb|const className ObjName|

\verb|className const ObjName|

\verb|const|对象是不允许直接修改其内部成员变量的数值的，在编译期就会报错，也不能通过方法成员来修改。

\verb|const|对象也不能调用非\verb|const|的方法。

\subsection{对象数组}
声明对象数组的方法，与声明基本数据类型的数组的格式，是相同的。

对于股票类Stock来说，声明一个Stock数组，如下

\verb|Stock mystock[4];|

如果类包含多种构造函数，那么可以对不同的元素分别使用不同的构造函数。

未被显式地构造函数初始化的元素，则被默认构造函数初始化

要创建类对象数组，则这个类必须有默认构造函数。

\section{类的成员函数}
\subsection{定义类的成员函数}
成员函数的定义，与常规函数的定义，十分相似，也有函数头和函数体，也可以有返回类型和参数，但是也有不同之处。

\begin{itemize}
    \item 定义成员函数时，使用作用域解析运算符(\verb|::|)来标识函数所属的类
    \item 类方法可以访问类的\verb|private|成员
\end{itemize}

仍然以Stock类为例，\verb|update()|成员函数的函数头，可能是下面的形式

\verb|void Stock::update(double price);|

将\verb|update()|标识为了是某个类的成员函数，另外，说明其他类也可以使用同名的一个\verb|update()|函数，只要前面加上了类名

标识符\verb|update()|具有"类作用域"(class scope)，Stock类的其他成员函数不必使用作用域解析运算符也可以使用符\verb|update()|方法，因为他们是同属于一个类的。

\verb|Stock::update()|是函数的限定名(qualified name)，而\verb|update()|是全名的缩写，是非限定名(unqualified name)，只能在类作用域中使用。

\subsection{const成员函数}
在函数原型后面加上\verb|const|关键字，表示该方法为只读函数，不能修改类对象的数据成员。\textbf{优点}是可以提高程序的可读性。

传给const成员函数的this指针，是指向\verb|const|对象的\verb|const|指针。

\verb|const|成员函数，不能够修改任何成员变量，除非成员变量被\verb|mutable|修饰符修饰。\verb|mutable|修饰符使得const函数的行为有了一些灵活性。相当于提醒编译器，这个成员变量比较特殊，就不要进行任何只读检查了。

在成员函数调用的过程中，都有一个 this 指针被当做参数隐性地传递给成员函数(可能通过栈，也可能通过CPU寄存器)。这个this指针，指向调用这个函数的对象(这样，成员函数才能找到成员变量的地址，从而对其进行操作)。这个this指针，是个 const指针，不能修改其指向(你不希望这个对象的函数，修改了那个对象的成员变量，对吧？)。

传递给const成员函数的this指针，指向一个const对象。也就是说，在const成员函数内部，这个this指针是一个指向const对象的const指针。通过第二节的探讨，相信大家已经能够明白，为什么const成员函数不能修改任何成员变量了。

\subsection{内联方法}
定义位于类声明中的函数，自动称为内联函数，例如上文中的\verb|Stock::set_tot()|函数

类声明通常将短小的成员函数作为内联函数

也可以在类声明之外定义内联函数作为成员函数，只要在类实现的部分中定义函数时使用\verb|incline|限定符

\begin{lstlisting}
class Stock
{
private:
    void set_tot();
public:
    ...
};

inline void Stock::set tot()
{
    total val = shares * share_val;
}
\end{lstlisting}

内联函数的特殊规则要求在每个使用它的文件中都要对其进行定义。因此，应该把内联函数的定义放在头文件之中。

\subsection{使用成员函数}
成员函数如何使用？哪个对象调用成员函数，成员函数就会对哪个对象起作用。

看下面的代码示例。

\begin{lstlisting}
Stock kate, joe;

kate.show();
joe.show();
\end{lstlisting}

所创建的每一个新对象都有自己的存储空间，用于存储其内部变量和类成员。

但是同一个类的所有对象，共享一组类方法，每种方法只有一个副本。

不同的对象，调用同一个方法，他们会执行同一个代码块，只是应用与不同的数据。

\subsection{类方法可以访问私有成员}
方法的另一个特性是，他可以访问类的私有成员。

使用普通的非成员函数访问数据成员，编译会报错。

C++提供"友元函数"这一技巧来让非成员函数来访问类的私有成员。

\section{静态成员}
\subsection{静态成员变量}
静态成员变量，即，声明时前面有关键字\verb|static|修饰的成员变量。其含义是，所有这个类的对象公用一个变量。

\subsubsection{不能在头文件中初始化静态成员变量}
不能在类的声明文件中初始化静态成员变量。声明中只描述了如何分配内存，但是并没有具体地分配内存。

在类声明(\verb|*.h|文件)中初始化，由于多次出现在\verb|#include|语句中，所以初始化语句会出现好多遍，引发错误。

有些编译器也会将这种情况直接列为错误。比如如果在类声明中直接初始化静态数据成员\verb|static int e_ = 1;|，将报错
\begin{lstlisting}
error: ISO C++ forbids in-class initialization of non-const static member 'A::e_'
\end{lstlisting}

\subsubsection{在类定义中进行初始化，只初始化1次}
所以一般在类定义中进行初始化，即\verb|*.cpp|文件中。而且，此时不需要再加上\verb|static|关键字。

对于不能在类声明中初始化静态数据成员的一种例外情况是，静态数据成员为整型或者枚举型\verb|const|。

静态常量数据成员 \verb|static const| 可以直接在类内初始化。

声明了静态数据成员，就必须为其初始化，否则报 \verb|error: undefined reference|。

\subsubsection{静态成员必须为常量表达式}
静态成员必须为字面值常量类型的constexpr。

所谓的字面值类型就是通常遇到的：算术类型，引用，指针等。字面值常量类型就是const型的算术类型，引用，指针等。

所谓的 \verb|constexpr|，就是常量表达式，指值不会改变且在编译过程中就能得到计算结果的表达式。比如字面值，或者用常量表达式初始化的 \verb|const| 对象也是常量表达式。为了帮助用户检查自己声明/定义的变量的值是否为一个常量表达式，C++11新规定，允许将变量声明为 \verb|constexpr| 类型，以便由编译器来进行验证变量是否为常量表达式。

对于非\verb|const|整数类型的初始值，如果它是常量表达式的话，我们需要手工在前面添加修饰符\verb|constexprt|
\begin{lstlisting}
// static double rate = 6.5;  // wrong statement.
constexprt static const double rate = 6.5
\end{lstlisting}

或者在类声明之外初始化这个值
\begin{lstlisting}
class Example {
public:
    static const double rate;
    static const int vecSize = 20;
};

const double Example::rate = 1.0;
\end{lstlisting}

\paragraph{(C++17)与类型推导auto连用}
详见第\pageref{(C++17)可用于类的非静态成员初始化}页第\ref{(C++17)可用于类的非静态成员初始化}节

\subsection{静态成员函数}
使用关键字\verb|static|将成员函数声明为静态的，会产生如下影响。

静态数据成员在类中使用\verb|static|声明，在类方法定义文件中初始化但不再使用\verb|static|关键字。

\paragraph{调用方式}
一般来说，不能通过对象来调用静态成员函数，如果静态成员函数是在公有部分声明的，则可以使用类名和作用域解析运算符来调用它。

\paragraph{关于this指针}
静态成员函数不能使用\verb|this|指针。

\paragraph{只能使用静态成员}
由于静态成员函数不与特定的对象相关联，因此只能使用静态数据成员，不能调用对象的非静态成员，也不能使用非静态成员函数。

可以使用静态成员函数来设置类级(classwide)标记，以控制某些类接口的行为。例如，类级标志可以控制显示类内容的方法所使用的格式。

\section{构造函数与析构函数}
\subsection{构造函数}
\subsubsection{认识构造函数}
C++的目标之一是让使用类对象就像使用基本数据类型一样，简单。然而，根据上一节介绍的那些简单知识，并不能实现如此简单的使用类对象。

主要原因在于，原则上，类的某些数据成员是私有的，无法通过声明时完成初始化的工作(这里初始化指的主要是赋初值)，形如基本数据类型那样\verb|int a = 1;|来赋初值。

所以C++提供了一种特殊的成员函数，构造函数(constructor)，来构造新对象，将值赋给他们的数据成员。

构造函数的函数名，要求与类名相同，这样编译器可以将之识别为构造函数。例如，对于股票类Stock，其构造函数为\verb|Stock::Stock()|

\subsubsection{声明和定义构造函数}

回到我们上文一直在用的股票类Stock(第\ref{从实例开始:股票Stock类}节)。

创建Stock类的一个对象时，需要为其3个私有数据成员提供值，(\verb|total_val|成员是由其他三个值计算出来的，所以它不需要提供值)

我们这里给出一种构造函数的可能形式，假设程序员指向设置\verb|company|成员，其他值只设置为0即可，因此其他值使用默认参数的方式来完成。那么，我们的构造函数的原型，可能有如下的形式。

\verb|Stock::Stock(const string & co, long n = 0, double pr = 0.0);|

构造函数是没有返回值的，也不要声明为\verb|void|类型

私有的构造函数，是不能用来初始化一个类对象的。

构造函数的参数名，一般不使用类的成员名。否则可能会出现形如\verb|share = share;|这样的情况。

为了避免这种混乱，一般在数据成员名的前面加上\verb|m_|前缀，或者是使用\verb|_|后缀，例如\verb|m_company|或者\verb|company_|

\subsubsection{使用构造函数}

使用构造函数来初始化对象，主要有两种方式，这两种写法是等价的

\begin{enumerate}
\item 一种是显式地调用构造函数，如下

\verb|Stock food = Stock("World Cabbage", 250, 1.25);|

\item 另一种方式是隐式地调用构造函数，如下

\verb|Stock food("World Cabbage", 250, 1.25);|
\end{enumerate}

每次创建类对象，甚至是使用\verb|new|动态分配内存时，C++都使用类的构造函数。

将构造函数与\verb|new|一起使用的方法，如下

在这种情况下，这个对象是没有名称的，但是可以通过指针来操作对象。

\verb|Stock *pstock = new Stock("Electroshock Games", 18, 19.0);|

\subsection{默认构造函数}
\subsubsection{使用场景}
默认构造函数是在未显式地提供初始值时，用来创建对象的构造函数。

也就是说，默认构造函数是用于下面这种声明类对象时的构造函数。

\verb|Stock fluffy_the_cat;|

无参数的默认构造函数，为每一个没有定义构造函数的成员都递归地生成一个默认构造函数。

\subsubsection{空白的默认构造函数}
如果没有定义构造函数，编译器会给一个空白的默认构造函数。对于股票类来说，就是\verb|Stock::Stock() {};|。这种构造函数，在创建对象时，起到的作用，就类似于\verb|int a;|。

\subsubsection{不能有参数列表}
默认构造函数不能有参数列表，因为是默认的。

\subsubsection{有时候必须给出一个默认构造函数的声明}
如果定义了构造函数，则必须再给出一个默认构造函数的定义，否则会报错。(就像\verb|switch|分支都要有一个\verb|default|分支，必须保证集合的完整性)

有两种做法
\begin{enumerate}
    \item 给已有的构造函数的所有参数提供默认值
    \item 通过函数重载来定义另外一个构造函数，一个完全没有参数的构造函数。
\end{enumerate}

\subsubsection{(C++11)强制声明一个默认构造函数}
虽然有自己定义的构造函数，但是若想使用默认构造函数，则可以写\verb|className() = default;|

\verb|=default|符号，只能用于特殊成员函数，即，默认构造函数、拷贝构造函数、析构函数等。

如果使用在普通的成员函数上，则编译器会报错。

例如下面的代码示例

\begin{lstlisting}
class B { 
public: 
    // Error, func is not a special member function. 
    int func() = default;  
    // Error, constructor B(int, int) is not a special member function. 
    B(int, int) = default;  
    // Error, constructor B(int=0) has a default argument. 
    B(int = 0) = default;  
};
\end{lstlisting}

\subsubsection{默认构造函数如何处理POD}
如前文所述，无参数的默认构造函数，为每一个没有定义构造函数的成员都递归地生成一个默认构造函数。

但是这里会有一些问题，那就是在涉及到C里面原有的数据类型时(这些类型会被成为POD, plain old data)，比如\verb|int|和\verb|float|，并不会为其生成一个默认构造函数。因为C里面就没有这个东西，所以不能破坏C本身。

这样，这些成员就不会被初始化为0，包括\verb|void*|和\verb|object*|等指针。会发生的事情就是，内存这里原来是什么值，现在就会是什么值，所以会是一个随机数，这就产生了UB(Undefined Behavior)。

所以，如何才能也让编译器为这些POD也生成默认构造函数？在声明该成员的时候，写成\verb|int m_weight{0};|或者\verb|int m_weight{};|

\subsection{析构函数}
\subsubsection{认识析构函数}
创建对象后，程序负责一直追踪这个对象，对象过期后，程序将执行一个特殊的成员函数，用于清理对象所占用的内存，这个成员函数称为析构函数(destructor)

\subsubsection{声明和定义析构函数}
析构函数的名称，是在类名前加上波浪线\verb|~|

析构函数没有返回值和声明类型

与构造函数所不同的是，析构函数没有参数。例如，对于股票类Stock来说，其析构函数的原型必须是\verb|~Stock();|这样的

\subsubsection{使用析构函数}
如果没有人工定义析构函数，编译器将会自动给出一个析构函数。

什么时候调用析构函数？这由编译器决定，通常不会在代码中显式地调用析构函数，但是也有部分特殊情况会显式地调用析构函数。

\subsection{成员初始化列表}\label{成员初始化列表}
\subsubsection{C++11的成员初始化列表}
从概念上来说，调用构造函数时，对象将在括号中的代码被执行之前被创建。因此，调用构造函数将导致程序先给其成员变量分配内存。然后，程序流程进入到括号中，使用常规的赋值方式将值存储到内存中。

我们将构造函数分为两个先后过程：
\begin{enumerate}
    \item 初始化阶段。创建对象，给成员变量分配内存。调用构造函数时，创建对象，隐式或者显示的初始化各数据成员(给其成员变量分配内存)
    \item 赋值计算阶段。进入构造函数体中执行一般赋值与计算操作。
\end{enumerate}

成员初始化列表，只能用于构造函数，不能用于其他类方法。

成员初始化列表的工作是在对象创建的时候完成的，此时还没有执行括号中函数体里面的代码。

\subsubsection{定义成员初始化列表}
如果\verb|Class_name|是一个类名，他里面有成员\verb|mem1, mem2, mem3|三个成员，则类构造函数可以使用如下的语法来初始化其数据成员。下面的代码将\verb|mem1|初始化为n，将\verb|mem2|初始化为2，将\verb|mem3|初始化为\verb|n*m + 2|

\begin{code_example}
\begin{lstlisting}
Class_name::Class_name(int n, int m) : mem1(n), mem2(0), mem3(n*m + 2) {
    ...
}
\end{lstlisting}
\end{code_example}

\subsubsection{初始化的顺序}
数据成员被初始化的顺序，和他们出现在类声明中的顺序相同，与初始化器里的排列顺序无关。

比如下面的代码示例，虽然初始化列表中\verb|j|在\verb|i|的前面。但是由于声明中\verb|i|在\verb|j|的前面，所以会先构造成员\verb|i|，再构造成员\verb|j|。

\begin{lstlisting}
class X {
public:
    X(int val): j(val), i(j)
    {}
public:
    int i;
    int j;
};
\end{lstlisting}

\subsubsection{从编译器实现的角度来看成员初始化列表}
对于下面这样的代码

\begin{lstlisting}
// **在构造函数体内初始化**
class Person {
public:
    Person() {
        m_name = 0;
        m_age = 0;
    }
private:
    String m_name;
    int m_age;
};
\end{lstlisting}

类\verb|Person|的构造函数，会先产生一个临时性的\verb|String|对象，然后将他初始化，然后通过赋值运算符，将它赋值给成员\verb|m_name|。然后再摧毁临时性的\verb|String|对象。

以伪代码的形式表现，如下所示

\begin{lstlisting}
// C++ 伪代码
Person()
{
    //调用String的缺省构造函数
    m _name.String::String();
    
    //产生临时性对象
    String temp = String(0);
    
    //逐一成员地拷贝 _name
    m_name.String::operator=( temp );
    
    //摧毁临时性对象
    temp.String::~String();
    
    m_age = 0;
}
\end{lstlisting}

而使用初始化列表的方式

\begin{lstlisting}
class Person
{
public:
    Person() :  m_age(0) , m_name(0)
    {}
private:
    String m_name;
    int m_age;
};
\end{lstlisting}

在构造\verb|Person|对象时，不会有创建临时对象再销毁的现象发生。写成伪代码如下所示

\begin{lstlisting}
// C++ 伪代码
Person()
{
    //调用String(int) 构造函数
    m_name.String::String(0);
    m_age = 0;
}
\end{lstlisting}

\subsubsection{必须使用成员初始化列表的情况}
\begin{enumerate}
    \item \verb|const|类成员或者被声明为引用的类成员。对于类成员中含有一个\verb|const|(常量)对象时，或者是一个引用时，他们也必须要通过成员初始化列表进行初始化。因为这两种对象要在声明后马上初始化，而在构造函数中，做的是对他们的赋值，这样是不被允许的。所以必须在执行到构造函数体之前，即在对象被创建时进行初始化。
\begin{lstlisting}
class A
{
    priate:
    const int a;            //const修饰成员
    public:
    A():a(10){}             //初始化
};

class B
{
    private:
    int &b;                //被声明为引用
    public:
    B(int b):b(b){}     //初始化
}

\end{lstlisting}

    \item 初始化成员是对象(包含继承)。如果一个类成员本身就是一个类或者一个结构，并且这个成员只有一个带参数的构造函数，没有无参数的构造函数；这时要对这个类成员进行初始化，就必须调用这个类成员的带参数的构造函数，如果没有初始化列表，那么他将无法完成第一步，就会报错。
\begin{lstlisting}
class A
{
    public:
    A (int, int){
    };
    private:	//类成员为x，y
    int x;
    int y;
};
class B 
{
    public:
    B():a(1,2),z(z){       //初始化
    };
    private:
    A a; //B的另一个类成员为一个A的类对象， 类对象作为类成员
    int z;
};

\end{lstlisting}
    
    \item 子类初始化父类的私有成员
\begin{lstlisting}
class Test{
    public:
    Test(){};
    Test (int x){ int_x = x;};
    void show(){cout<< int_x << endl;}
    private:
    int int_x;
};
class Mytest:public Test{
    public:
    Mytest() ：Test(110){
        //Test(110);            //  构造函数只能在初始化列表中被显示调用，不能在构造函数内部被显式地调用
    };
};
int main()
{
    Test *p = new Mytest();
    p->show();
    return 0;
}

\end{lstlisting}
\end{enumerate}

\subsubsection{使用成员初始化列表}

在C++11中，可以将成员初始化列表语法用于类，只要提供与某个构造函数的参数列表匹配的内容，并用大括号将他们括起来。

\verb|Stock hot_tip = {"Derivatives Plus Plus", 100, 45.0};|

另外，C++11还提供了名为\verb|std::initialize_list|的类，可将其用作函数参数或者方法参数的类型。这个类可以表示任意长度的列表，只要所有列表项的类型都相同，或者可以转换为相同的类型。

\subsubsection{类内初始化(in-class initialization)(C++11)}
C++11允许以更直观的方式进行初始化

\begin{lstlisting}
class Class_name
{
    int mem1 = 10;
    const int mem2 = 20;
}
\end{lstlisting}

这与如下的成员初始化列表是等价的。

\begin{lstlisting}
Class_name::Class_name() : mem1(10), mem2(20) {...}
\end{lstlisting}

类内初始化，又称为就地初始化。就地初始化的优先级比成员初始化列表要高，而成员初始化列表的优先级又比构造函数初始化要高。

\subsection{其他}
默认情况下，将一个对象赋给同类型的另一个对象时，C++将每个成员对应复制过去，与使用结构体类似。

\subsubsection{const成员函数}
我们知道，在C++中，若一个变量声明为\verb|const|类型，则试图修改该变量的值的操作都被视编译错误

面向对象程序设计中，为了体现封装性，通常不允许直接修改类对象的数据成员。若要修改类对象，应调用公有成员函数来完成。为了保证\verb|const|对象的常量性，编译器须区分不安全与安全的成员函数(即区分试图修改类对象与不修改类对象的函数)

在C++中，只有被声明为\verb|const|的成员函数才能被一个\verb|const|类对象调用

要声明一个\verb|const|类型的类成员函数，只需要在成员函数参数列表后加上关键字\verb|const|，例如

\begin{lstlisting}
class Screen {
    public:
    char get() const;
};

char Screen::get() const {
    return _screen[_cursor];
}
\end{lstlisting}

值得注意的是，把一个成员函数声明为\verb|const|可以保证这个成员函数不修改数据成员，但是，如果据成员是指针，则\verb|const|成员函数并不能保证不修改指针指向的对象，编译器不会把这种修改检测为错误

\paragraph{小结}

\begin{enumerate}
\item \verb|const|成员函数可以访问非\verb|const|对象的非\verb|const|数据成员、\verb|const|数据成员，也可以访问\verb|const|对象内的所有数据成员
\item 非\verb|const|成员函数可以访问非\verb|const|对象的非\verb|const|数据成员、\verb|const|数据成员，但不可以访问\verb|const|对象的任意数据成员
\item 作为一种良好的编程风格，在声明一个成员函数时，若该成员函数并不对数据成员进行修改操作，应尽可能将该成员函数声明为\verb|const| 成员函数
\end{enumerate}


\subsection{为什么要提出内存布局类型这一概念}
在C++11中，平凡类型(Trivial Type)、平凡可复制类型(TrivialCopyable)、标准布局类型(Standard-layout Type)是描述类在内存中布局特性的术语，它们与类的构造、拷贝、赋值和销毁行为有关，也影响着类的内存布局和对齐方式。

提出这些概念，是因为C++并没有用一种统一的方式来管理所有的类，而是，将他们进行分类，并且在存在提升性能、简化操作的可能性的时候，使用效率更高的方式。另外，也有兼容C语言传统的考虑。

\subsubsection{优点}
\begin{enumerate}
    \item 内存效率与性能：平凡类型允许简单的位拷贝，提高内存使用效率和程序性能。
    \item 跨平台兼容性：标准布局类型确保了不同编译器和硬件平台上的一致性，增强了代码的可移植性。
    \item C语言兼容性：平凡类型与C语言结构体的内存布局兼容，便于C++与C语言代码的互操作。
    \item 模板编程：这些概念提供了对模板元编程的支持，允许编写高效且通用的模板代码。
    \item 多线程安全：平凡类型的操作线程安全，简化了并发编程中同步和锁的需要。
    \item 简化代码与资源管理：减少了复杂资源管理的需求，使得代码更简洁，易于维护。
    \item 避免未定义行为：通过明确的类型特性，减少了因对象操作导致的未定义行为，增强了程序的安全性。
\end{enumerate}

\subsubsection{分类}
从内存布局角度，类可以分为以下几种
\begin{enumerate}
    \item \textbf{平凡类型}(Trivial Type)：构造函数、析构函数、拷贝构造函数和赋值运算符都没有自定义实现，完全由编译器提供的默认行为。这意味着这些类的对象可以像基本数据类型一样被创建和销毁。
    \item \textbf{平凡可复制类型}(TrivialCopyable)：是平凡类型的一个扩展，这些类虽然严格来说不是平凡类型，但是由于他们仍然可以通过简单的位拷贝(bitwise copy)来复制，那么它也可以被认为是平凡可复制的。
    \item \textbf{标准布局类型}(Standard-layout Type)：指那些在内存布局上满足一定规则的类。这些规则包括所有非静态成员的访问权限必须相同，类不能有虚函数或虚基类，且所有基类也必须是标准布局类型。标准布局类型的一个重要特性是它们的内存布局在不同的编译器和平台上是一致的，保障了跨平台性。
\end{enumerate}

\subsection{平凡类型}\label{平凡类型}
鉴别一个类是否属于平凡类型，以下几个条件应该\textbf{都不满足}。
\begin{enumerate}
    \item 显式定义了4种特殊成员函数(默认构造函数，拷贝构造函数，赋值运算符，默认析构函数)
    \item 类里有非静态非POD的数据成员。类中如果有动态内存分配(如指针成员)或需要特殊资源管理的成员，也不是平凡类型。
    \item 有基类。
    \item 没有虚函数。
\end{enumerate}

如果定义了空白构造函数，也不能称之为平凡类型。即，\verb|A() {}|则类A不能称为平凡类型。如果\verb|A() = default;|，则类A仍可能是平凡类型(还要考察其他条件)。

\subsection{标准布局类型}
标准内存布局在内存布局上满足特定规则，这些规则确保了类型在不同的编译器和平台上具有一致的内存布局。

因为它的内存布局要确定，因此它的定义比较严格一切跟编译器实现相关的不确定因素都不能有。

\begin{enumerate}
    \item 子类和父类不能都有非静态成员，因为子类和基类内存布局不确定。
    \item 非静态成员必须有相同的访问权限，不同的访问权限段之间内存布局不确定(c++11)。
    \item 不能有虚函数，虚继承，引用等跟编译器实现不确定的东西。
    \item 不能有有两个一样的基类等影响空基类优化的东西。
    \item 所有成员得到内存布局也得是Standard-Layout Type等
\end{enumerate}

使用\verb|std::is_standard_layout|可以检查一个类型是否是标准布局类型。这在跨平台编程中非常重要，因为它保证了类型在不同平台上的ABI(应用二进制接口)兼容性。

\subsection{POD}
POD是为了C++和C类型兼容而设的概念，同时包含Trivial Type和Standard-Layout Type的含义，随着时代的发展，POD的概念逐渐被更细致的Trivial Type，Standard-Layout Type概念所替代。

\section{this指针}
简单的情况下，每个类成员函数只涉及一个对象，即调用它的对象。但是有的时候可能涉及到两个对象，这种情况下就要使用\verb|this|指针

\verb|this|指针，指向用来调用成员函数的对象。

\subsubsection{股票类Stock需要解决的问题}

需要写一个方法，查看两个Stock对象，并返回股价较高的那个对象的引用。

假设该方法命名为\verb|topval()|。那么，\verb|stock1.topval()|将访问\verb|stock1|对象的数据，\verb|stock2.topval()|将访问\verb|stock2|对象的数据。如果希望该方法对两个对象进行比较，则必须将第二个对象作为参数传递给他，我们采取按引用传递的方式，也即，\verb|topval()|方法需要使用一个类型为\verb|const Stock &|的参数

那么，如何将方法的返回值传递给调用的程序呢？可以放方法返回一个引用，该引用指向股价较高的那个对象，因此，总结起来，我们得出这个函数的原型如下

\verb|const Stock & topval(const Stock & s) const;|

该函数，隐式地访问一个对象，显式地访问另外一个对象，并返回其中一个对象的引用。另外做一些解释，括号中的\verb|const|不会修改被显式地访问的对象，而括号后面的\verb|const|表明，该函数也不会修改隐式地访问的对象。由于该函数返回了两个\verb|const|对象之一的引用，因此返回类型也应为\verb|const|引用。

(括号后面的\verb|const|的原理，可见"const成员函数"一部分)

有了这样的分析，我们可以这样使用\verb|topval()|函数，如下面的两种写法，都是可以的，都会把股价较高的对象返回给\verb|top|

\verb|top = stock1.topval(stock2);|

\verb|top = stock2.topval(stock1);|

\subsubsection{如何实现这样的topval()函数}

上一段中已经设计了\verb|topval()|函数的函数原型，但是如何编写函数定义呢。

看下面的代码示例，反映出了编写\verb|topval()|函数时中途可能遇到的问题。

\begin{lstlisting}
const Stock & Stock::topval(const Stock & s) const
{
	if (s.total_val > total_val)
		return s;
	else
		return ???;
}
\end{lstlisting}

\verb|s.total_val|是按引用传递进来的对象的股价，\verb|total_val|是调用该方法的对象的股价。如果\verb|s.total_val|的股价更高，则返回该对象，如果\verb|total_val|的股价更高，那么该返回什么呢？

这个问题，用通俗的话来叙述，就是"我在调用我的方法内部，如何表示我"

因此，C++给出了\verb|this|指针，指向用来调用成员函数的对象。上面的问题就得以解决了，返回\verb|this|指向的对象即可，即\verb|*this|(加上\verb|*|表示指针指向的值)。

\subsubsection{this指针}

每个成员函数都有一个\verb|this|指针，包括构造函数和析构函数

如果方法需要引用整个调用对象，则可以使用表达式\verb|*this|

在函数的括号后面使用\verb|const|限定符将\verb|this|限定为\verb|const|，这样将不能使用\verb|this|来修改对象的值。



\section{特殊成员函数}
原来C++共有5个特殊成员函数，C++11又新增了2个

\begin{enumerate}
    \item 默认构造函数，如果没有定义构造函数的话，由编译器给出
    \item 默认析构函数，如果没有定义的话
    \item 默认拷贝构造函数，如果没有定义的话
    \item 赋值运算符(拷贝赋值运算符)，如果没有定义的话
    \item 取值运算符，如果没有定义的话
    \item (C++11)移动构造函数(move constructor)
    \item (C++11)移动赋值运算符(move assignment operator)
\end{enumerate}

\subsection{默认构造函数}
\subsubsection{基本形式}
默认构造函数的基本形式为

\begin{lstlisting}
Class_name::Class_name() { }
\end{lstlisting}

默认构造函数，不接受任何参数，也不执行任何操作。这说明，如果使用默认构造函数创建一个对象，那么它的内存是未知的(既没有初始化，也并不是完全没初始化，就是未定义，未知的)

如果定义了构造函数，C++也就不会给出默认构造函数了。

\subsubsection{带参数的默认构造函数}
带参数的构造函数也可以是默认构造函数，但是前提条件是所有的成员都有默认值。

\begin{lstlisting}
Class_name::Class_name(int n = 0) { m_int = 0; }
\end{lstlisting}

\subsubsection{唯一性}
一个类只有一个默认构造函数。例如代码示例\ref{只能有一个默认构造函数}这样的情况是不允许的，这会产生二义性。

\begin{code_example}
\label{只能有一个默认构造函数}
\begin{lstlisting}
Class_name() { m_int = 0; }
Class_name(int n = 0) { m_int = 0; }
\end{lstlisting}
\end{code_example}

\subsection{拷贝构造函数}
\subsubsection{默认拷贝构造函数基本形式}
默认拷贝构造函数的基本形式为

\begin{lstlisting}
Class_name(const Class_name &);
\end{lstlisting}

它接收一个指向类对象的常量引用作为参数。

拷贝构造函数用于将一个对象复制到新创建的对象中。它用于初始化过程中(包括按值传递参数的时候)，而不是常规的赋值过程。

\subsubsection{什么时候生成默认拷贝构造函数}
在编译期生成的。

如果用户没有自行定义拷贝构造函数，则编译器会给出一个默认拷贝构造函数。

\begin{enumerate}
    \item 类的成员变量如果有默认拷贝构造函数，则编译器会为该类生成一个默认拷贝构造函数
    \item 基类有默认拷贝构造函数，则编译器会给派生类生成默认拷贝构造函数。
    \item 类成员函数中存在虚函数，则编译器会给该类生成默认拷贝构造函数。因为涉及到虚函数表指针的拷贝问题。    
    \item 基类中有虚函数，则编译器会给派生类生成默认拷贝构造函数。
\end{enumerate}

\subsubsection{什么时候调用拷贝构造函数}\label{什么时候调用拷贝构造函数}
新建一个对象并将其初始化为同一个类的现有的对象的时候，会调用拷贝构造函数。

假设\verb|t2|是一个\verb|Class_name|类型的对象，那么下面的四种情况，都将会调用拷贝构造函数。

\begin{lstlisting}
Class_name t1(t2);                      // (1)
Class_name t1 = t2;                     // (2)
Class_name t1 = Class_name(t2);         // (3)
Class_name * p_t1 = new Class_name(t2); // (4)
\end{lstlisting}

程序为某个类对象生成副本时，编译器会使用拷贝构造函数。比如，按值传递或函数返回一个对象时，编译器都会为之生成一个临时对象，这时都会使用拷贝构造函数。

因此，可以解释，按值传递和按引用传递的区别，按引用传递会节省时间和空间。

\subsubsection{默认拷贝构造函数的功能与缺陷}
\paragraph{不复制静态成员}
默认拷贝构造函数，逐个复制非静态成员，复制的是成员的值。这种按照成员的值进行的复制，也称为\textbf{浅拷贝}。

静态成员(关键字\verb|static|)，因为它不单独属于某一个类对象，而是属于整个类。在调用默认拷贝构造函数的时候，对静态成员的影响也需要特殊关注。

\paragraph{与动态内存管理}
总的来说，涉及到动态内存管理的时候(比如某个成员是指针，文件句柄，构造函数中带有\verb|new|运算符等)，默认拷贝构造函数，其功能是不全面的，或者说有缺陷的。

如果成员本身也是类对象，那么则会使用这个类对象的相应的拷贝构造函数(递归地调用成员的拷贝构造函数)。

\subsubsection{有静态成员时，浅拷贝可能存在的缺陷}
对于一个成员里包含静态成员的类来说，情况可能有所不同，比如代码示例\ref{拷贝构造函数示例代码里的类}中的类\verb|StringBad|有一个\verb|static|成员\verb|num_strings|，其本来作用是，记录总共创建了多少这样的类对象。因此，在拷贝构造函数中，如果执行了一次复制操作，生成了一个副本，但是却不对这个变量做任何操作，是很明显不合理的。合理的情况是，进行一次复制，那么这个计数器就要增加1，所以对于这一点的修改，可以看代码示例\ref{带静态成员的拷贝构造函数}

\begin{code_example}
\label{拷贝构造函数示例代码里的类}
\begin{lstlisting}
class StringBad
{
private:
    char * str;
    int len;
    static int num_strings;
public:
    ...
}
\end{lstlisting}
\end{code_example}

\begin{code_example}
\label{带静态成员的拷贝构造函数}
\begin{lstlisting}
StringBad::StringBad(const StringBad & s)
{
    num_string++;
    ...  // 暂略
}
\end{lstlisting}
\end{code_example}

\subsubsection{有指针成员时，浅拷贝可能存在的缺陷}
仍然以代码示例\ref{拷贝构造函数示例代码里的类}中的类\verb|StringBad|为例，它里面有一个指针成员\verb|char * str|。存储着这个字符串类所保存的字符串的字面值，以\verb|char|数组的形式存储，其中\verb|str|存储着数组的开头，\verb|len|存储着数组的长度。

如果对\verb|StringBad|对象进行一次复制，那么\verb|str|成员，必须指向新的\verb|char|数组的开头(这与默认拷贝构造函数就产生了矛盾，因为后者是执行浅拷贝的，单纯复制每一个成员的值)。如果指向原来\verb|StringBad|对象的\verb|char|数组的开头，那么如果旧对象析构之后，该指针将无法指向有效值。

因此，就不能再使用默认拷贝构造函数了，需要手动定义一个拷贝构造函数，完整版的拷贝构造函数见代码示例\ref{带指针成员的拷贝构造函数}。

如果类中包含了使用\verb|new|初始化的指针成员，应当定义一个拷贝构造函数，以复制指向的数据，而不是指针，这被称为\textbf{深拷贝}，与浅拷贝相对应。

\begin{code_example}
\label{带指针成员的拷贝构造函数}
\begin{lstlisting}
StringBad::StringBad(const StringBad & st)
{
    num_string++;               // 静态计数器自增
    len = st.len;               // 获取长度
    str = new char [len + 1];   // 声明一个新的char[]存放副本的字符串
    std::strcpy(str, st.str);   // 复制字符串char[]
    ...  // 暂略
}
\end{lstlisting}
\end{code_example}

\subsection{赋值运算符}
ANSI C允许结构赋值，C++允许类对象赋值，这是通过自动为类重载赋值运算符来实现的，这种运算符的原型如下

\begin{lstlisting}
Class_name & Class_name::operator=(const Class_name &);
\end{lstlisting}

它接收并返回一个指向类对象的引用。

\subsubsection{默认赋值运算符}
与默认拷贝构造函数相似，默认赋值运算符的隐式实现，也是对成员进行逐个复制(即，浅拷贝)，静态成员除外。

\subsubsection{何时调用赋值运算符}

将已有的对象赋给另外一个对象时，将使用重载的赋值运算符。

初始化对象时，并不一定会调用赋值运算符。下面的情况，只会使用拷贝构造运算符。但是这也不一定，取决于编译器的具体实现。

\begin{lstlisting}
Class_name t1 = t2;
\end{lstlisting}

\subsubsection{改造默认赋值运算符(从浅拷贝到深拷贝)}
将浅拷贝优化为深拷贝，与改造拷贝构造函数的思想类似。

\begin{itemize}
    \item 由于目标对象可能引用了以前分配的数据，所以函数应该使用\verb|delete[]|来释放这些数据
    \item 函数应当避免将对象赋给自身，否则，给对象重新赋值前，释放内存操作也可能删除对象自身的内容
    \item 函数返回一个指向调用对象的引用
\end{itemize}

仍然以代码示例\ref{拷贝构造函数示例代码里的类}中的类\verb|StringBad|为例，代码示例\ref{带静态成员和指针的类的赋值运算符}给出了它的赋值运算符的改进写法。

首先检查赋值运算符右边的地址\verb|&st|是否与接收者(\verb|this|)相同，如果相同则直接返回\verb|*this|

如果地址不同，则释放\verb|str|指向的内存，稍后将一个新字符串的地址赋值给\verb|str|成员。如果不释放，这块内存会被浪费掉(除非程序结束运行，操作系统回收这部分内存)

赋值运算符本身并不创建新的对象，所以静态计数器\verb|num_strings|不需要调整

\begin{code_example}
\label{带静态成员和指针的类的赋值运算符}
\begin{lstlisting}
StringBad & StringBad::operator=(const StringBad & st)
{
    if (this == &st)
        return *this;
    delete [] str;
    len = st.len();
    str = new char [len + 1];
    std::strcpy(str, st.str);
    return *this
}
\end{lstlisting}
\end{code_example}

\subsubsection{进一步优化赋值的过程}

有时，使用赋值运算符为类对象赋值的时候，同时还伴随着"创建一个临时副本，并调用拷贝构造函数将他复制到目标类对象中"的过程。如果频繁使用，这种处理效率比较低下。

所以，\textbf{能否优化一下赋值运算符}，让它省略创建副本，调用拷贝构造函数这两个过程，直接进行赋值。仍然以代码示例\ref{拷贝构造函数示例代码里的类}为例，提供一种优化了的写法，供参考。

\begin{lstlisting}
String & String::operator=(const char * s)
{
    delete [] str;
    len = std::strlen(s);
    str = new char[len + 1];
    std::strcpy(str, s);
    
    return *this
}
\end{lstlisting}


\subsubsection{(C++11)拷贝赋值运算符(copy assignment operator)}\label{拷贝赋值运算符(copy assignment operator)}
基本形式如下

\begin{lstlisting}
className & className::operator=(const className & a);
\end{lstlisting}

\subsubsection{(C++11)移动赋值运算符(move assignment operator)}\label{移动赋值运算符(move assignment operator)}
基本形式如下

\begin{lstlisting}
className & className::operator=(className && a);
\end{lstlisting}


\section{聚合类(aggregate class)}\label{聚合类(aggregate class)}
\subsubsection{聚合类的定义}
聚合类(aggregate class)，也有译成聚合体类型。指的是一系列特殊的类。

聚合体类型，一般指的是\verb|array|类型，或者是满足以下条件的自定义的类
\begin{enumerate}
    \item 只包含有\verb|public|的直接非静态数据成员
    \item 没有用户自己定义的构造函数或者继承的构造函数(inherited constructors)
    \item 没有虚基类或者\verb|private|基类或者\verb|protected|基类
    \item 没有虚成员函数
\end{enumerate}

\subsubsection{举例说明}
\begin{lstlisting}
// 对于struct，如果不加访问修饰关键字(public, protected, private),默认为public
// 符合4个条件，属于聚合类
struct AggregateClass
{
    int height;
    string name;
};

// 符合4个条件，属于聚合类
class AggregateClass
{
    public:
    int height;
    string name;
};

/ 对于class，如果不加访问修饰关键字(public, protected, private),默认为private
// 不符合条件 1)，不属于聚合类
class NoneAggregateClass
{
    int height;
    string name;
};

// 有构造函数，不满足条件 2)，不属于聚合类
class NoneAggregateClass
{
    public:
    NoneAggregateClass()
    {
        
    }
    int height;
    string name;
};

// 存在类中初始化器，不满足条件 3)，不属于聚合类
class NoneAggregateClass
{
    public:
    int height {180}; // 用花括号对连同括号里内容作为一个整体叫做初始化器
    string name {"Jim"};
};

class BaseClass
{
    
};

// 有基类，不满足条件 4)，不属于聚合类
class NoneAggregateClass : public BaseClass
{
    public:
    int height;
    string name;
};


// 有虚函数，不满足条件 4)，不属于聚合类
class NoneAggregateClass
{
    public:
    virtual ~NoneAggregateClass()
    {
        
    }
    
    int height;
    string name;
};
\end{lstlisting}

\subsubsection{为什么要单独强调这一概念}
聚合类的特征，由于他没有太多复杂的结构和功能，使用者可以直接访问(读写)其成员变量。也可以在聚合类上使用特殊的初始化语法，即列表初始化。

\begin{lstlisting}
class AggregateClass
{
    public:
    int height;
    string name;
};

int main()
{
    AggregateClass inst {180, "Jim"}; // 特俗的初始化语法，亦即 初始化器列表
    std::cout << "Height : " << inst.height << ", Name: " << inst.name << std::endl; // 读取成员变量的值
    inst.height = 181； // 覆写成员变量的值
    inst.name = "Tom"; // 覆写成员变量的值
    return 0;
}
\end{lstlisting}

C++很多功能不是设计出来，而是总结出来，比如这个聚合类。最开始我们认为每个类都应该维护自己的不变式，都有自己的构造函数和析构函数。后来发现其实很多时候并不是这样，我们只是需要维护一个组合关系，比如数据库表中的字段。良好设计记录的字段之间大部分没有依赖关系。这还不是一个个例。除了领域概念本身，其他领域模型都多少是这种情况。作为接口的类都是这样。早期用POD来说明这个用法，后来发现这个只是一个特例，慢慢总结出来这个aggregate class范围就大一点。

可以说，聚合类，是一种符合POD思想的类，他足够的简单，易复制，易初始化。

\section{初始化}
\subsection{默认初始化(default-initialization)}
在没有使用初始化器来初始化变量的时候，这个场景被称为默认初始化。
\begin{lstlisting}
T object ﻿;	// (1)	
new T	// (2)	
\end{lstlisting}

默认初始化，在以下几种情况下发生
\begin{enumerate}
    \item 声明具有自动、静态或线程局部存储期的变量时，但是不带初始化器
    \item 使用\verb|new|创建动态存储期变量，但是不带初始化器
    \item 当调用构造函数时，用于构造的初始化器中，没有给出基类或者非静态成员时。
\end{enumerate}

\subsubsection{效果}
\begin{enumerate}
    \item 如果\verb|T|是类类型，或者非POD类型，那么会考虑构造函数，可能会进行空白参数列表的重载决议。调用决议得到的构造函数以进行初始化。
    \item 如果\verb|T|是一个数组，那么其每一个成员都按默认初始化处理。
    \item 除此之外，不会进行任何其他种类的初始化。
\end{enumerate}

\subsubsection{不进行任何初始化，会产生未定义行为UB}
如果不对对象进行任何初始化，那么该对象具有不确定值，直到该值被替换。如果求值产生了不确定值，那么行为未定义。

\subsection{值初始化(value-initialization)}
值初始化是在以空初始化式构造对象时进行的初始化。
\begin{lstlisting}
T ()	(1)	
new T ()	(2)	
Class::Class(...) : member () { ... }	(3)	
T object {};	(4)	(since C++11)
T {}	(5)	(since C++11)
new T {}	(6)	(since C++11)
Class::Class(...) : member {} { ... }	(7)	(since C++11)
\end{lstlisting}

\subsubsection{对于聚合类型，使用花括号的时候}
如果\verb|T|是聚合类型，使用空花括号\verb|{}|进行初始化的时候，会发生聚合初始化，而不是值初始化。

\subsubsection{(C++11)有接受std::initializer\_list的构造函数}
如果\verb|T|是没有默认构造函数但带有接受 \verb|std::initializer_list| 的构造函数的类类型，那么会进行列表初始化。(C++11起)

\subsubsection{效果}
\begin{enumerate}
    \item 如果\verb|T|没有默认构造函数，那么他会被默认初始化。
    \item 如果\verb|T|是具有默认构造函数的类类型，那么会进行零初始化。然后如果它拥有非平凡的默认构造函数，那么就会默认初始化它。
    \item 如果\verb|T|是数组，那么其每一个成员都按值初始化处理。
    \item 否则，会零初始化该对象。
\end{enumerate}

\section{类与动态内存管理\enspace 使用new和deleet}
让程序在运行时决定内存分配，而非早在编译时决定，即动态地进行程序内存管理，使用\verb|new|和\verb|delete|。

\subsubsection{在类中成对使用new和delete}
在构造函数中\verb|new|了一块内存，那么一定要在析构函数中对应地\verb|delete|掉这一块内存。

如果使用\verb|new[]|分配内存，则对应地，也要使用\verb|delete[]|。如果不是用\verb|new[]|初始化的指针，如果再用\verb|delete[]|释放内存的话，会发生不确定的结果，可以看下面的代码示例，这三种情况都属于未定义的情况，会发生什么是未知的，所以要避免这样的写法出现。

\begin{lstlisting}
char words[15] = "bad idea";
char * p1 = words;
char * p2 = new char;
char * p3;

delete [] p1;
delete [] p2;
delete [] p3;
\end{lstlisting}

如果有多个构造函数，则他们也都要统一\verb|new|的使用，或者都是\verb|new|，或者都是\verb|new[]|

\section{类的作用域}

前文中，我们提到了全局作用域和局部作用域，C++中引入了一种新的作用域，\textbf{类作用域}。

在类中定义的名称，如类的数据成员名和成员函数名，的作用域都是整个类。作用域在整个类的名称，只是在该类中是已知的，在类外是不可知的。因此，可以在不同的类中使用相同的类成员名而不会引起冲突。

类作用域也意味着，不能从外部直接访问类的成员，包括公有成员函数。也就是说，要调用公有成员函数，必须通过对象。

在定义成员函数时，也必须使用作用域解析运算符。

在想要使用类成员的时候，必须根据情况选择，直接成员运算符\verb|.|、间接成员运算符\verb|->|和作用域解析运算符\verb|::|

\subsection{作用域为类的常量}

本节解决的问题是，同一个类的不同对象可能会公用一个变量，如果每一个对象都圈出一块内存来保存相同的值，是一种资源浪费，能否公用同一块内存来表示这一个变量，不管有多少个对象，都只使用这一块内存呢？

使用\verb|static|关键字

\begin{lstlisting}
class Backery
{
private:
	static const int Months = 12;
	...
}
\end{lstlisting}

这里将会创建一个名为\verb|Months|的常量，这个常量将会与其他静态变量存储在一起，而不是在存储在对象中。因此，只有一个\verb|Months|常量，将被所有\verb|Backery|共享。

\subsection{作用域内枚举(C++11)}\label{作用域内枚举(C++11)}

\subsubsection{与传统枚举的区别}

作用域内枚举的创建方式与传统枚举的区别主要在于，前者使用\verb|enum class|，后者仅使用\verb|enum|即可

\subsubsection{避免发生重定义}

传统枚举可能存在的问题:两个枚举定义的枚举量可能发生冲突。

假如我们已有一个枚举量的定义如下\verb|Sex|，如果再在其下面定义一个\verb|Student|的枚举量，则编译器会报错，提示有重定义的情况发生

\begin{lstlisting}
enum Sex
{
    Girl,  
    Boy 
};

// 错误，编译器提示Girl, Boy重定义
enum Student
{
    Girl,  
    Boy 
};
\end{lstlisting}

如果使用作用域内枚举，如下

\begin{lstlisting}
enum class Sex
{
    Girl,  
    Boy 
};

enum class Student
{
    Girl,  
    Boy 
};

int main(int argc, char *argv[])
{
    Sex a = Sex::Gril; 
    Student b = Student::Gril;
    //两者处于不同作用域下，不会重定义
}
\end{lstlisting}

\subsubsection{强类型的特点}
作用域内枚举是强类型的，不能隐式转换为整数类型，但是可以显式转化为整数类型。

\begin{lstlisting}
enum class Sex
{
    Girl,  
    Boy 
};

int main(int argc, char *argv[])
{
    Sex a = Sex::Gril;
    int d1 = a; // 错误，无法从“Girl”隐式转换为“int”。
    int d2 = int(a); // 正确，显示将enum class转换为整数
    return 0;
}
\end{lstlisting}

当然，可以使用\verb|static_cast|实现限定作用域的枚举类型转换。

\subsubsection{可以指定底层类型}
底层类型默认为\verb|int|类型。

\begin{lstlisting}
enum class Color : int { Red = 1, Green = 2, Blue = 3 };
\end{lstlisting}

\subsubsection{(C++17)可以直接使用列表初始化}
在C++17中，对于有底层类型的枚举类型对象，可以直接使用列表初始化。在创建枚举类型对象时，直接使用花括号\verb|{}|来初始化它们。
\begin{lstlisting}
enum class Color : int { Red = 1, Green = 2, Blue = 3 };
Color color = {1}; // 使用列表初始化，等价于 Color color = Color::Red;
\end{lstlisting}

\subsubsection{(C++20)使用using扩展作用域}
在C++20中，可以使用\verb|using|将强枚举的作用域进行扩展，类似于以往使用在命名空间上的形式。
\begin{lstlisting}
using enum Color
\end{lstlisting}

\section{抽象数据类型}

抽象数据类型(abstract data type, ADT)

ADT以比较通用的方式描述数据类型，没有规定实现细节。

比如，使用栈来存储数据，就是使用ADT的一个例子。

\subsection{以栈为例子}

栈存储了多个数据项，栈是一种数据的容器。

栈可以进行如下操作

\begin{enumerate}
\item 可以创建一个空栈
\item 可以将数据项压入栈顶
\item 可以将数据项从栈顶弹出
\item 可以查看栈是否填满
\item 可以查看栈是否为空
\end{enumerate}

将上述描述转换为一个类的声明，用公有的成员函数来表示栈的各种操作的接口，用私有的数据成员来存储栈的数据，这就体现了ADT的思想。

\section{运算符重载}

\subsection{认识运算符重载}

运算符重载是C++的多态的一种体现形式。

联系到之前讲到的函数多态的概念，函数多态或称为函数重载，指的是用户能够定义多个名称相同但是特征标(参数列表)不同的函数。

运算符重载，将重载的概念扩展到运算符上，允许赋予C++的运算符以多种含义。

实际上，很多C++的运算符已经是被重载了的，比如\verb|*|，将\verb|*|应用于地址，将得到存储在这个地址的值，将\verb|*|用于两个数字时，得到的是他们的乘积。

格式：\verb|opertatorA(argument-list)|

这里的\verb|A|需要替换成要重载的运算符，例如\verb|operator+()|，而且必须是C++里有效的运算符，而不能是不存在的运算符。

\subsection{重载的一些限制}

\begin{enumerate}
\item 重载后的运算符必须至少有一个操作数是用户定义的类型。
\item 不能违反原来的句法规则，例如，不能将$ \% $ 重载成使用一个操作数的运算符，也不能修改运算符原有的优先级
\item 不能创建新的运算符
\item 有些运算符不允许重载
\end{enumerate}

\subsubsection{重载累加运算符}

累加运算符具有前缀和后缀两个版本，我们这里不讨论他的区别，只讨论如何重载这两个版本。

重载前缀版本，运算符重载的函数声明应该写\verb|className & operator++() {}|

重载后缀版本，应该写成\verb|className & operator++(int) {}|

可以看出，主要区别在于\verb|int|

函数返回值的类型其实是不固定的，根据需求，但是常用的写法是返回`\verb|className &|

\section{友元}

\subsection{认识友元}

C++中，对于对象的私有部分，访问它是会受到限制的，一般来说，程序员会设计公有方法来合理恰当地访问私有成员。

但是有的时候，这种死板的限制会带来不变。因此C++提供新的技巧来解决这些不便之处，那就是友元。

友元主要可分为3种

\begin{enumerate}
\item 友元函数
\item 友元类
\item 友元成员函数
\end{enumerate}

通过让函数称为类的友元，可以赋予该函数与类的成员函数相同的访问权限。

\subsection{友元函数}\label{友元函数}

\subsubsection{为什么要引入友元}

假设我们要在程序里实现一个24小时制的时间值，我们设计了一个类\verb|Time|来描述时间值。

\begin{lstlisting}
class Time
{
	private:
		int hours;
		int minutes;
	public:
		Time();
		Time(int h, int m = 0);
		void AddMin(int m);
		void AddHr(int h);
		void Reset(int h = 0, int m = 0);
		Time operator+(const Time & t) const;
		Time operator-(const Time & t) const;
		Time operator*(double n) const;
		void Show() const;
};
\end{lstlisting}

这其中有些成员和方法的功能，是顾名思义的，所以在这里不再详述。

把加法和乘法的运算符重载定义，列举如下

\begin{lstlisting}
Time Time::operator+(const Time & t) const
{
	Time sum;
	sum.minutes = minutes + t.minutes;
	sun.hours = hours + t.hours + sum.minutes / 60;
	sum.minutes %= 60;
	return sum;
}
Time Time::operator*(double mult) const
{
	Time result;
	long totalminutes = hours * mult * 60 + minutes * mult;
	result.hours = totalminutes / 60;
	result.minutes = totalminutes % 60;
	return result;
}
\end{lstlisting}

对比一下，加法和乘法对于\verb|Time|类来说的运算符重载。这两者本身都是二元运算符，都需要两个操作数，加法两侧两个操作数的类型是相同的，都是\verb|Time|类，而乘法，左右两个操作数的类型是不同的，是一个\verb|Time|类型和\verb|double|类型之间的运算

对于形如\verb|A = B * 2.75;|这样的语句来说，他会被转换为\verb|A = B.operator*(2.75)|这样的语句进行运行。

那么，对于\verb|A = 2.75 * B;|这样的语句呢，此时\verb|*|运算符的左侧，并不是\verb|Time|类了，而是\verb|double|类

从程序功能的层面，我们理所应当地希望\verb|A = B * 2.75;|和\verb|A = 2.75 * B;|是等价的，但是如何让编译器、程序能够实现这一功能呢？

难道说，要告诉每一个使用我们代码的人，不能按照\verb|2.75 * B|这样来使用\verb|Time|类，而必须按照\verb|B * 2.75|这样来使用。这显得很，蹩脚。这样的代码，很脆弱。

那么，我们尝试把这一功能(指的是，对乘法运算符进行重载，让他可以将\verb|Time|类和\verb|double|类进行相乘)，换成用非成员函数来实现。语句\verb|A = 2.75 * B;|就可以转换成形如\verb|A  = operator*(2.75, B);|这样的形式。这样的非成员函数，可能会有这样的形式\verb|Time operator*(double m, const Time & t);|

使用非成员函数可以按所需的顺序获得操作数。

但是，遇到了新问题。非成员函数并不能直接访问类的私有数据。

所以，C++就发明了友元函数这一技术，使得，某些非成员函数，也能够访问类的私有数据。

通过这个例子，我们就描述了一个，能够想到引入友元的一个需求的示例。

\subsubsection{创建友元函数}

创建友元函数，将其原型放在类的声明中，并且在原型的前面加上关键字\verb|friend|

\verb|friend Time operator*(double m, const Time & t);|

虽然函数\verb|operator*()|是在类声明中声明的，但是它并不是成员函数，因此调用时不能使用成员运算符，但是他与成员函数的访问权限是相同的。

在编写函数定义时，也不能添加\verb|Time::|这样的限定符。因此，只有在类声明中，才能决定哪一个函数是友元函数。类声明仍然控制了哪些函数可以访问私有数据，所以说，友元技术的引入，并不违反OOP"数据隐藏"的思想。

定义的时候，也不要写\verb|friend|关键字。

\subsection{友元类}

C++ Primer Plus (6th edition)原书第602页，第15.1.1节

\subsubsection{应用场景}

什么时候需要用到友元类呢？什么时候希望一个类成为另一个类的友元呢？

友元类的所有方法，都可以访问原始类的私有成员和保护成员。

或者还可以有其他的限制，只将特定的成员函数指定为另一个类的友元。

举个例子，电视机和遥控器。遥控器是可以控制电视机的(这里就指的是，读写遥控器的私有成员)

\paragraph{与继承的关系}

遥控器并不是电视机，反之亦然。所以并不属于公有继承。

遥控器也不是电视机本身的一部分，所以私有继承和保护继承也不适用。

\subsubsection{使用友元类}

\verb|friend class Remote;|，将\verb|Remote|类成为友元类。

友元声明，可以位于公有、私有或保护部分。他的位置并不重要。

\begin{code_example}\label{使用友元类}
\begin{lstlisting}
// tv.h -- Tv and Remote classes
#ifndef TV_H_
#define TV_H_

class Tv
{
public:
    friend class Remote; // Remote can access Tv private parts
    enum {Off, On};
    enum {MinVal,MaxVal = 20};
    enum {Antenna, Cable};
    enum {TV, DVD};
    Tv(int s = Off, int mc = 125) : state(s), volume(5),
        maxchannel(mc), channel(2), mode(Cable), input(TV) {}
    void onoff() {state = (state == On)? Off : On;}
    bool ison() const {return state == On;}
    bool volup();
    bool voldown();
    void chanup();
    void chandown();
    void set_mode() {mode = (mode == Antenna)? Cable : Antenna;}
    void set_input() {input = (input == TV)? DVD : TV;}
    void settings() const; // display all settings
private:
    int state; // on or off
    int volume; // assumed to be digitized
    int maxchannel; // maximum number of channels
    int channel; // current channel setting
    int mode; // broadcast or cable
    int input; // TV or DVD
};

class Remote
{
private:
    int mode; // controls TV or DVD
public:
    Remote(int m = Tv::TV) : mode(m) {}
    bool volup(Tv & t) { return t.volup();}
    bool voldown(Tv & t) { return t.voldown();}
    void onoff(Tv & t) { t.onoff(); }
    void chanup(Tv & t) {t.chanup();}
    void chandown(Tv & t) {t.chandown();}
    void set_chan(Tv & t, int c) {t.channel = c;}
    void set_mode(Tv & t) {t.set_mode();}
    void set_input(Tv & t) {t.set_input();}
};
#endif
\end{lstlisting}
\end{code_example}

\subsection{友元成员函数}

\subsubsection{含义}

仅让特定的类成员成为其他类的友元，而不是整个类成为另一个类的友元

必须注意声明的先后顺序。

接着代码示例\ref{使用友元类}，这次我们修改一下需求，仅仅让\verb|Remote::set_chan()|成为\verb|Tv|类的友元。

所以\verb|Tv|类中要这样声明友元成员函数。

\begin{lstlisting}
class Tv
{
    friend void Remote::set_chan(Tv & t, int c);
    ...
};
\end{lstlisting}

\subsubsection{循环依赖现象(circular dependencies)}

编译器如果要处理\verb|friend void Remote::set_chan(Tv & t, int c);|这条语句，他就必须知道\verb|Remote|的定义，这就要求\verb|Remote|的定义应该在\verb|Tv|的前面。

而\verb|Remote|对象中提到了\verb|Tv|对象，这又要求\verb|Tv|的定义必须在\verb|Remote|的前面。

这就产生了\textbf{循环依赖(circular dependencies)}

解决方法之一是使用\textbf{前向声明(forward declaration)}，让代码按如下的顺序排列。

\begin{lstlisting}
class Tv; // forward declaration
class Remote { ... };
class Tv { ... };
\end{lstlisting}

像下面这样的排列顺序是不行的，因为\verb|Tv|类里有\verb|Remote|类的一个方法被作为了友元方法函数，在此之前就应该看到\verb|set_chan()|的声明，仅仅有\verb|class Remote;|是不够的。

\begin{lstlisting}
class Remote; // forward declaration
class Tv { ... };
class Remote { ... };
\end{lstlisting}

\paragraph{如果有内联代码}

如果\verb|Remote|中有内联代码，并且调用了\verb|Tv|的方法，此时，编译器必须要看到\verb|Tv|类的声明。

解决办法是，不再使用内联的形式，而让该方法的定义，放在\verb|Tv|类的声明的后面。

通过在方法定义上使用\verb|incline|关键字，仍然可以使其成为内敛方法。

\subsection{更多友元关系}

\subsubsection{两个类互为友元}

下面的代码中，\verb|Remote|对象能够影响\verb|Tv|对象，反过来也一样。

\begin{code_example}\label{两个类互为友元}
\begin{lstlisting}
class Tv
{
friend class Remote;
public:
    void buzz(Remote & r);
    ...
};

class Remote
{
friend class Tv;
public:
    void Bool volup(Tv & t) { t.volup(); }
    ...
};

inline void Tv::buzz(Remote & r)
{
    ...
}
\end{lstlisting}
\end{code_example}

\subsubsection{同时是两个类的友元}

某个函数，需要能欧访问两个类的私有数据。它可以是一个类的成员函数，同时是另一个类的友元。但是更合理的做法是，让它同时是这两个类的友元。

\begin{code_example}\label{同时是两个类的友元}
\begin{lstlisting}
class Analyzer; // forward declaration
class Probe
{
    friend void sync(Analyzer & a, const Probe & p); // sync a to p
    friend void sync(Probe & p, const Analyzer & a); // sync p to a
    ...
};

class Analyzer
{
    friend void sync(Analyzer & a, const Probe & p); // sync a to p
    friend void sync(Probe & p, const Analyzer & a); // sync p to a
    ...
};

// define the friend functions
inline void sync(Analyzer & a, const Probe & p)
{
    ...
}

inline void sync(Probe & p, const Analyzer & a)
{
    ...
}
\end{lstlisting}
\end{code_example}

\section{嵌套类(nested class)}\label{嵌套类}

\subsubsection{应用场景}

将类的声明放在另外一个类里。

作为包含者的类，它可以在成员函数中使用和创建被包含者的类的对象。

当且仅当被包含者的类的声明位于包含者的公有部分，才能在包含者的外部使用被包含者，而且要使用作用域解析运算符。

\subsubsection{声明嵌套类的位置}

嵌套类声明的位置，决定了嵌套类的作用域，决定了程序的哪些位置可以创建和使用这个类的对象。

如果嵌套类是在包含者类的私有部分声明的，则只有包含者知道他的存在，只有包含者能够使用它。包含者派生出的类也看不到而且也不能使用嵌套类。

如果嵌套类是在包含者类的保护部分声明的，派生类是可以知道嵌套类，而且可以使用嵌套类，但是外部世界仍然看不到嵌套类，而且不能使用嵌套类。

如果嵌套类是在包含者类的公有部分声明的，那么，不管是包含者类、派生类还是外部世界，都能够看到嵌套类，而且都能够使用嵌套类。外部世界使用嵌套类时，必须使用作用域解析运算符。

\section{和类有关的数据类型转换问题}\label{和类有关的数据类型转换问题}

本节讨论，C++如何处理用户定义的类型，与其他类型之间的转换问题，包括基础数据类型，也包括其他的用户定义的类型。

复习一下C++如何处理内置的基础数据类型的。

详细内容请查阅第\ref{类型转换}节。

\begin{enumerate}
\item 将一个基础数据类型的变量，赋值给另外一个基础数据类型的变量，如果此二者兼容，那么C++将这个值自动转换为负责接受的变量的类型。
\item 如果 二者不兼容，不会发生自动类型转换。如果程序员有意愿的话，可以使用强制类型转换(参考第\ref{强制类型转换}节)。

\begin{lstlisting}
int * p = 10;  // 这样的语句是非法的
int * p = (int *) 10;  // 使用强制类型转换，就是合法的，至于这种写法有没有意义是另外一回事
\end{lstlisting}
\end{enumerate}

\subsection{隐式转换}

下面的构造函数，用于将\verb|double|类型转换为自己编写的类\verb|Stonewt|(原书中采用的代码示例)

\verb|Stonewt(double lbs);|

也就是说，对于这样的代码

\begin{lstlisting}
Stonewt myCat;
myCat = 19.6;
\end{lstlisting}

编译器将使用构造函数\verb|Stonewt(double)|创建一个临时的\verb|Stonewt|对象，并将19.6作为临时对象的初始化的值，然后，采用逐个成员赋值的方式，将这个临时对象的内存复制到已有的对象\verb|myCat|中。这一过程被称为\textbf{隐式转换}。

隐式转换都会发生在哪些场景呢：

\begin{enumerate}
\item 将\verb|Stonewt|对象初始化为\verb|double|值时
\item 将\verb|double|值赋值给\verb|Stonewt|对象时
\item 将\verb|double|值传递给接受\verb|Stonewt|参数的函数时
\item 返回值被声明为\verb|Stonewt|的函数试图返回\verb|double|值时
\item 在上述的任意一种情况下，使用可转换为\verb|duoble|类型的内置类型时。(先发生某个内置类型向\verb|double|类型的转换，再发生\verb|double|向\verb|Stonewt|的隐式转换)
\end{enumerate}

这个过程是不需要显式的强制类型转换参与。

只有仅接收1个参数的构造函数，才能作为一种转换函数来使用，如上面的代码示例。

如果有两个参数，则不能，例如\verb|Stonewt(int stn, double lbs);|

但是如果其中一个参数有默认值，如\verb|Stonewt(int stn, double lbs = 0);|，那么它可以用于转换\verb|int|

\subsection{关键字explicit}

如果给构造函数的声明增加一个关键字\verb|explicit|，则禁止了他所对应的类型转换。

\begin{lstlisting}
explicit Stonewt(double lbs);    
\end{lstlisting}

\begin{enumerate}
\item \verb|myCat = 19.6;|，如果使用了关键字\verb|explicit|，则这种方法是非法操作了，因为关闭了这种隐式类型转换。
\item \verb|myCat = Stonewt(19.6);|，合法操作，是显式类型转换。
\item \verb|myCat = (Stonewt) 19.6;|，合法操作，显示类型转换的旧式写法。
\end{enumerate}

\subsubsection{小心二义性}

如果\verb|Stonewt(double)|和\verb|Stonewt(long)|同时存在，则可能出现二义性。

包括其他类似的情况，都可能产生二义性。

\subsection{自定义类向内置类型的转换}

前文已经解决了由内置类型向自定义的类的类型转换问题，那么，可不可以进行相反的类型转换？

必须使用特殊的C++运算符函数，\textbf{转换函数}

\begin{lstlisting}
Stonewt wells(20, 3);
double star = wells;
\end{lstlisting}

右侧是\verb|Stonewt|类型，左侧是\verb|double|类型。类似于运算符重载的形式，我们可以认为，\verb|Stonewt|类型是输入，而\verb|double|类型是输出。

如果要转换为\verb|typeName|类型，则需要以如下形式声明一个转换函数

\begin{lstlisting}
operator typeName();
\end{lstlisting}

必须注意以下几点

\begin{itemize}
\item 转换函数必须是类方法
\item 转换函数不能指定返回类型，但是函数中要有\verb|return|语句，要有返回值。
\item 转换函数不能有参数
\end{itemize}

在C++98中，关键字\verb|explicit|不能用于转换函数，但是在C++11中，消除了这种限制。

转换函数的定义，写法可参考如下代码示例。

\begin{lstlisting}
Stonewt::operator int() const
{
    return int (pounds + 0.5);
}

Stonewt::operator double() const
{
    return pounds;
}
\end{lstlisting}

\subsection{(incomplete)转换函数与友元函数之间的关系}

C++ Primer Plus (6th edition)原书第419页，第11.6.2节

\section{函数返回对象}

当成员函数或独立的函数，返回一个对象时，有几种返回方式

\begin{itemize}
\item 返回指向对象的引用
\item 指向对象的\verb|const|引用
\item \verb|const|对象
\end{itemize}

\subsubsection{返回指向对象的const引用}

返回指向\verb|const|对象的引用可以提高效率

但是对于何时使用这种方式，有一些需要注意的限制。

代码示例如下，假设有这样的需求，编写一个函数\verb|Max()|，返回两个\verb|Vector|类对象中比较大的一个，其使用方式如虾

\begin{lstlisting}
Vector force1;
Vector force2;
Vector max;
max = Max(force1, force2);
\end{lstlisting}

有以下两种实现方式都是可行的

另外有需要注意的点

\begin{enumerate}
\item 返回对象(如version1)，将会调用拷贝构造函数，但是返回引用不会(如version2)，第二个版本的效率较高。
\item 引用所指向的对象，应该在调用函数执行时存在。
\item 传入的参数如果都声明为\verb|const|引用，则返回的引用也应该是\verb|const|的
\end{enumerate}

\begin{lstlisting}
// version 1
Vector Max(const Vector & v1, const Vector & v2)
{
    ...
    if ( ... )
        return v1;
    else
        return v2;
}

// version 2
const Vector & Max(const Vector & v1, const Vector & v2)
{
    ...
    if ( ... )
        return v1;
    else
        return v2;
}
\end{lstlisting}

\subsubsection{返回指向对象的非const引用}

重载的赋值运算符，和重载的与\verb|cout|一起使用的\verb|<<|运算符，都是返回非\verb|const|对象的例子。

重载赋值运算符使用非\verb|const|引用，一方面为了提高效率，另一方面可以用于连续赋值。

\subsubsection{返回对象}

如果被返回的对象似乎被调用函数中的局部变量，则不应该使用引用方式返回它，因为在被调用函数执行完毕时，局部对象将要调用其析构函数。

此时会调用拷贝构造函数，来创建一个负责中转的副本，这是无可避免的开销。

举例子，是对\verb|+|加法运算符的重载

\begin{lstlisting}
Vector Vector::operator+(const Vector & b) const
{
    return Vector(x + b.x, y + b.y);
}
\end{lstlisting}

\subsubsection{返回const对象}

为\verb|Vector|类重载了\verb|+|运算符之后，就可以像下面这样使用它

\begin{lstlisting}
net = force1 + force2;                              // 1: three Vector objects
force1 + force2 = net;                              // 2: dyslectic programming
cout << (force1 + force2 = net).show() << endl;     // 3: demented programming
\end{lstlisting}

写法3中，\verb|cout|中输出临时对象的信息，然后该临时对象被删除。

写法2中，\verb|force1 + force2|的结果会生成一个临时对象，然后\verb|net|的值会被赋给这个临时对象，当这一部分的代码块运行结束后，这个临时对象又消失了。等于说，程序什么都没有做。

因此，对于这样的错误的语句\verb|if(force1 + force2 = net)|，编译器也能运行。

为了防止这种行为，有一种简单的解决办法，将返回类型声明为\verb|const Vector|，这样写法1仍然合法，但是写法2和写法3会被标记为非法。

\subsubsection{总结}

\begin{enumerate}
\item 如果要返回局部对象，则应该选择返回对象的写法，而不能返回指向对象的引用。
\item 如果要返回一个没有公有拷贝构造函数的类对象，必须返回一个指向这个对象的引用。
\item 如果类可以以上两种返回形式，应优先选择引用，因为效率更高。
\end{enumerate}

\section{指向对象的指针}

\subsubsection{使用new初始化对象}

\verb|Class_name * ptr = new Class_name(value);|将调用如下的构造函数，其中\verb|value|的类型为\verb|Type_name|

\begin{lstlisting}
Class_name(Type_name);
\end{lstlisting}

可能也会发生

\begin{lstlisting}
Class_name(const Type_name &);
\end{lstlisting}

下面的初始化方式将会调用默认构造函数

\begin{lstlisting}
Class_name * ptr = new Class_name;
\end{lstlisting}

\subsubsection{不同存储持续性的类对象何时释放内存}

如代码示例\ref{不同存储持续性的类对象何时释放内存}，

\begin{enumerate}
\item 代码块结束时，\verb|up|的析构函数会被调用。
\item 运行\verb|delete|语句时，\verb|*pt|会被释放
\item 整个程序结束后，静态对象\verb|nice|的析构函数会被调用
\end{enumerate}

\begin{code_example}
\label{不同存储持续性的类对象何时释放内存}
\begin{lstlisting}
class Act {...};
...
Act nice;  // external object
...
int main()
{
    Act * pt = new Act;  // dynamic object
    {
        Act up;  // automatic object
        ...
    }
    delete pt;
    ...
}
\end{lstlisting}
\end{code_example}

\subsubsection{定位new运算符和常规new运算符}

\part{面向对象编程}
\chapter{继承与多态}

\section{基类}

\subsection{什么是继承}

可以在不公开的情况下发布一个类，同时允许他人为该类增添新的功能

派生类对象包含基类对象

基类的公有成员成为派生类的公有成员

基类的私有部分，只能通过基类的公有和保护(关键字\verb|protected|)方法访问

需要添加派生类自己的构造函数

派生类可以添加新的成员和方法函数，以满足新的功能。这样，派生类就构成了既有基类原功能，又有部分新的功能的新的类。

基类和派生类

\begin{code_example}
\label{继承代码示例1}
\begin{lstlisting}
// base class
class TableTennisPlayer
{
private:
    string firstname;
    string lastname;
    bool hasTable;
public:
    ...
}

// inherited class
class RatedPlayer : public TableTennisPlayer
{
private:
    unsigned int rating;
public:
    ...
};
\end{lstlisting}
\end{code_example}

\subsection{继承中的构造函数}

\subsubsection{派生类的构造函数如何访问基类的私有部分}

既然访问基类的私有部分，必须使用基类的公有和保护方法来访问，那么如何定义派生类的构造函数呢？在生成一个派生类对象的时候，如何初始化基类的私有成员呢？

\begin{lstlisting}
RatedPlayer::RatedPlayer(unsigned int r, const string & fn,
                       const string & ln, bool ht) : TableTennisPlayer(fn, ln, ht)
{
    rating = r;        
}
\end{lstlisting}

如果省略上面所使用的成员初始化列表

\begin{lstlisting}
RatedPlayer::RatedPlayer(unsigned int r, const string & fn,
                        const string & ln, bool ht)
{
    rating = r;        
}
\end{lstlisting}

则上面的派生类的构造函数会与下面的代码相等价，即，使用基类的默认构造函数。

\begin{lstlisting}
RatedPlayer::RatedPlayer(unsigned int r, const string & fn,
                        const string & ln, bool ht)
{
    rating = r;        
}
\end{lstlisting}

\subsubsection{派生类调用基类的拷贝构造函数}

\begin{lstlisting}
RatedPlayer::RatedPlayer(unsigned int r, const TableTennisPlayer & tp)
                        : TableTennisPlayer(tp)
{
    rating = r;        
}
\end{lstlisting}

这样将会调用基类\verb|TableTennisPlayer|的拷贝构造函数，如果未定义则会由编译器默认给出一个。

如果设计到动态内存分配(即基类中使用了\verb|new|运算符)，则这容易产生隐患。(可以见第\ref{什么时候调用拷贝构造函数}节第\pageref{什么时候调用拷贝构造函数}页)

\subsubsection{小结}

关于派生类的构造函数，总结有如下要点需要注意

\begin{enumerate}
\item 首先会创建基类的对象，至于如何创建，取决于调用方式和基类自身的情况(构造函数是如何定义的)
\item 派生类构造函数应通过成员初始化列表将基类的信息传递给基类的构造函数
\item 派生类构造函数应该初始化派生类新增的数据成员
\end{enumerate}

\subsubsection{析构函数}

释放内存的过程，与申请内存的顺序刚好相反。先运行派生类的析构函数，将派生类的内存都释放，再调用基类的析构函数，将基类的内存释放。

\subsection{使用派生类}\label{使用派生类}

派生类与基类之间的特殊关系

\begin{enumerate}
\item 派生类可以使用基类的方法(私有方法除外)
\item 基类指针可以在不发生显示类型转换的情况下指向派生类对象
\item 基类引用可以在不发生显示类型转换的情况下指向派生类对象
\end{enumerate}

可以见下面的代码示例，示例中假设基类\verb|TableTennisPlayer|具有方法\verb|Name()|。虽然\verb|rt|和\verb|pt|分别声明为基类的引用和指针，但是他们都指向了派生类对象，并且可以调用其基类的方法。

\begin{lstlisting}
RatedPlayer rplayer1(1140, "Mallory", "Duck", true);
TableTennisPlayer & rt = rplayer;
TableTennisPlayer * pt = &rplayer;
rt.Name();
pt->Name();
\end{lstlisting}

但是要注意，这种具有单向性。基类的指针和引用，只能用来调用基类的方法，并不能调用派生类新增的方法。

\paragraph{扩展} 但是C++有更深入的技术可以提供更复杂的调用方式，比如说使用虚函数，具体请见第\pageref{虚函数}页第\ref{虚函数}节

\subsubsection{用派生类对象初始化基类对象}

先看下面的代码示例

\begin{lstlisting}
RatedPlayer rplayer1(1140, "Mallory", "Duck", true);
TableTennisPlayer rplayer2(rplayer1);
\end{lstlisting}

上面的代码示例中，第二行所匹配的构造函数的函数原型应该是如下的

\begin{lstlisting}
TableTennisPlayer(const RatedPlayer &);
\end{lstlisting}

就算没有这样的构造函数，但是也有默认拷贝构造函数。他接受的参数是一个基类引用，而代码示例中使用的是派生类引用。他将\verb|rplayer2|初始化为\verb|rplayer1|中所嵌套的那个基类对象。

\begin{lstlisting}
TableTennisPlayer(const TableTennisPlayer &);
\end{lstlisting}

\subsubsection{将派生对象赋值给基类对象}

先看下面的代码示例

\begin{lstlisting}
RatedPlayer rplayer1(1140, "Mallory", "Duck", true);
TableTennisPlayer rplayer2;
rplayer2 = rplayer1;
\end{lstlisting}

在这种情况下将会使用隐式重载赋值运算符

\begin{lstlisting}
TableTennisPlayer & operator=(const TableTennisPlayer &) const;
\end{lstlisting}

\section{公有继承}\label{公有继承}

\subsection{什么是公有继承}

\begin{enumerate}
\item 公有继承：常用，建立一种is-a的关系，即派生类对象同时也是一个基类对象，也可以更详细地称之为is-a-kind-of
\item 保护继承
\item 私有继承
\end{enumerate}

is-a关系是不可逆的，比如"水果不是香蕉"

has-a关系，比如午餐有水果，午餐和水果之间就是一种has-a关系

公有继承不能建立is-like-a关系。比如"凶手就像猛兽"，但是凶手并不是猛兽，所以不应该从"猛兽"中派生出"凶手"

公有继承也不能建立is-implemented-as-a关系。比如，可是使用数组来实现栈结构，但是并不能从数组中派生出栈，因为栈不是数组，数组索引也不是栈具有的属性。

公有继承不建立use-a关系，比如电脑可以使用打印机，但是并不能从电脑中派生出打印机。一般使用友元的方式来处理两个对象之间的通信。

\subsection{多态公有继承(Polymorphic Public Inheritance)}

在代码示例\ref{继承代码示例1}中，派生类对于基类的方法，没有做任何的修改。接下来，我们将作出一部分修改。这样会使得，同一个名称的方法，在基类和派生类中的表现是完全不同的。换个角度来说，方法的行为取决于调用该方法的对象。这种现象称为\textbf{多态}，同一个方法的行为随着上下文而异。

有两种重要的机制用于实现多态公有继承

\begin{itemize}
\item 在派生类中重新定义基类的方法
\item 使用虚方法，使用关键字\verb|virtual|
\end{itemize}

某一方法在基类中被声明为虚方法，使用了关键字\verb|virtual|之后，它再派生类中将自动成为虚方法。但同时应该对应地标记好哪一个是\verb|virtual|。

\subsubsection{实际应用场景}

某银行，需要开发两个类来表示两种不同的账户，一种是普通账户\verb|Brass|，一种是高级账户\verb|BrassPlus|，其中高级账户有一些普通账户所没有的功能。

他们所共同具有的信息包括：客户姓名，账号，当前余额

\begin{itemize}
\item 客户姓名
\item 账号
\item 当前余额
\end{itemize}

共同可以执行的操作为：创建账户，存款，取款，显示账户信息

而高级账户\verb|BrassPlus|还有一些特殊信息：透支上限，透支贷款利率，当前透支总额

\begin{itemize}
\item 透支上限，默认为500
\item 透支贷款利率，默认为\verb|11.125%|
\item 当前透支总额
\end{itemize}

高级账户与普通账户相比，并没有新增什么功能，但是操作的具体实现有所不同

\begin{itemize}
\item 对于取款操作，必须考虑透支保护
\item 对于显示操作，必须显示高级账户的其他信息
\end{itemize}

还有其他需求

\begin{itemize}
\item 银行可以修改客户的透支限额
\item 银行可以修改客户的透支贷款利率
\end{itemize}

\paragraph{使用public派生吗？}

是否满足is-a条件？当然满足，而且是不可逆的情况。

\paragraph{代码示例}

看一下代码示例\ref{多态公有继承代码示例1}

需要说明的内容

\begin{enumerate}
\item 可见，\verb|BrassPlus|类在\verb|Brass|类上面增加了3个私有数据成员和3个公有成员方法。
\item 两个类都声明了\verb|ViewAcct()|方法和\verb|Withdraw()|方法，但是二者的行为是不同的。这是多态公有继承的体现。
\item 在\verb|Brass|类的声明中我们看到了新的关键字\verb|virtual|。使用了关键字\verb|virtual|的方法被称为\textbf{虚方法(virtual method)}
\item \verb|Brass|类还声明了一个虚析构函数
\end{enumerate}

\begin{code_example}\label{多态公有继承代码示例1}
\begin{lstlisting}
#ifndef BRASS_H_
#define BRASS_H_
#include <string>

// Brass Account Class
class Brass
{
    private:
        std::string fullName;
        long acctNum;
        double balance;
    public:
        Brass(const std::string & s = "Nullbody", long an = -1,
                    double bal = 0.0);
        void Deposit(double amt);
        virtual void Withdraw(double amt);
        double Balance() const;
        virtual void ViewAcct() const;
        virtual ~Brass() {}
};

// Brass Plus Account Class
class BrassPlus : public Brass
{
    private:
        double maxLoan;
        double rate;
        double owesBank;
    public:
        BrassPlus(const std::string & s = "Nullbody", long an = -1,
                    double bal = 0.0, double ml = 500,
                    double r = 0.11125);
        BrassPlus(const Brass & ba, double ml = 500,
                    double r = 0.11125);
        virtual void ViewAcct()const;
        virtual void Withdraw(double amt);
        void ResetMax(double m) { maxLoan = m; }
        void ResetRate(double r) { rate = r; };
        void ResetOwes() { owesBank = 0; }
};
#endif
\end{lstlisting}
\end{code_example}

\subsection{关键字protected}

派生类的成员可以直接访问基类的\verb|protected|成员，但不能访问基类的\verb|private|成员

\section{虚函数}\label{虚函数}
在第\pageref{使用派生类}页第\ref{使用派生类}节中，我们提到了派生类与基类之间的特殊关系，尤其是在指针、引用上体现的特殊关系。主要围绕在"基类指针或引用可以在不发生显示类型转换的情况下指向派生类对象"这一点上。

但是在第\pageref{使用派生类}页第\ref{使用派生类}节中，我们只叙述了，基类指针或引用，可以调用基类的方法。在那个时候，派生类的方法和基类的方法是泾渭分明的，没有同名但不同行为的方法，所以一个方法名一定唯一地指向一个具体的函数。

但是对于多态公有继承来说，基类和派生类可能有同名但是表现行为完全不同的方法，那么，此时程序将如何决定他调用那一种方法呢？

我们分为如下两种情况来讨论。

\subsubsection{未使用virtual关键字} 如果是没有使用\verb|virtual|关键字的常规情况，程序将根据\textbf{指针、引用的类型}来选择具体是哪一种方法。

比如下面的代码示例。

\begin{lstlisting}
// behavior with non-virtual ViewAcct()
// method chosen according to reference type
Brass dom("Dominic Banker", 11224, 4183.45);
BrassPlus dot("Dorothy Banker", 12118, 2592.00);
Brass & b1_ref = dom;
Brass & b2_ref = dot;
b1_ref.ViewAcct(); // use Brass::ViewAcct()
b2_ref.ViewAcct(); // use Brass::ViewAcct()
\end{lstlisting}

\subsubsection{使用virtual关键字}

如果方法在声明的时候使用了\verb|virtual|关键字，则说明他是虚方法，则程序将根据\textbf{指针、引用所指向的对象的类型}来选择具体是哪一种方法。

下面的代码示例中，虽然\verb|b2_ref|是\verb|Brass&|类型的，但是由于他指向一个\verb|BrassPlus dot|，则他运行的方法是\verb|BrassPlus::ViewAcct()|

\begin{lstlisting}
// method chosen according to object type
Brass dom("Dominic Banker", 11224, 4183.45);
BrassPlus dot("Dorothy Banker", 12118, 2592.00);
Brass & b1_ref = dom;
Brass & b2_ref = dot;
b1_ref.ViewAcct(); // use Brass::ViewAcct()
b2_ref.ViewAcct(); // use BrassPlus::ViewAcct()
\end{lstlisting}

\subsubsection{虚析构函数}

虚析构函数的作用是，为了确保在释放派生类对象的时候，按照正确的顺序调用析构函数。(前面不是说了，派生类在析构的时候是先调用派生类的析构函数再调用基类的析构函数吗？是的，但是那是在没有使用\verb|virtual|关键字的情况下，而现在用了\verb|virtual|关键字，情况就有所不同)

\subsubsection{虚函数的工作原理}

请见第\ref{虚函数的工作原理}节

\subsection{其他注意事项}

如果当前类将会被用作为基类，则应将那些要在派生类中重新定义的类方法声明为虚的。

\subsubsection{构造函数}

构造函数不能是虚函数。派生类不继承基类的构造函数，所以将类的构造函数声明为虚函数是没有意义的。

\subsubsection{析构函数}

析构函数应当是虚函数，除非类不被用作基类。

看下面的代码示例。在调用\verb|delete pe|语句的时候，它会调用\verb|~Employee()|还是\verb|~Singer()|呢？

\begin{lstlisting}
class Employee;
class Singer : class Employee;
...
Employee * pe = new Singer;
...
delete pe;
\end{lstlisting}

这里就需要让\verb|Employee|的析构函数被定义为虚的，这样在调用\verb|delete pe|语句的时候，就会由动态联编而去调用\verb|~Singer()|，准确地释放掉应该释放的内存。

所以，基类的析构函数，除了默认析构函数以外，也应该给出一个虚析构函数，哪怕是里面什么都不做。

\begin{lstlisting}
virtual ~BaseClass() { };
\end{lstlisting}

\subsubsection{友元}

友元不能是虚函数，因为友元不是类成员，只有类成员才能是虚函数。

\subsubsection{如果没有重新定义}

如果派生类没有重新定义方法函数，则将使用该方法的基类的版本。

如果派生类位于派生链中，则将使用最新的虚函数版本。

\subsubsection{重新定义会隐藏方法，返回类型协变}
如果重新定义了方法，则会隐藏原来的方法。这与函数重载是不同的，函数重载并不会隐藏其他同名函数的。

如果重新定义一个派生类的方法，应当确保与原来的函数原型完全相同。如果返回类型是基类引用或指针，则可以修改为指向派生类的引用或指针。这种特性被称为\textbf{返回类型协变(covariance of return type)}，指的是，允许返回类型随着类的类型变化而变化。

如果基类声明本身就被重载了(有多个发生了函数重载的同名函数)，则应在派生类中重新定义所有的重载了的基类版本。如果定义的不够，也会发生隐藏。


\section{静态联编和动态联编}

联编(binding)，或者称为函数名联编，指的是，编译器将源代码中的函数调用，指定到具体的区执行某个代码块的行为，的过程。

虽然C++中有函数重载的技术，但是编译器仍然可以将函数调用一一对应起来。在编译器实现的联编，可以称之为静态联编(static binding)，又称为早期联编(early binding)。

而虚函数的存在，使得这个过程变得复杂起来。首先，就是编译期无法再确定该调用使用的是哪一个函数(基类的方法或者派生类的方法)。所以，就产生了动态联编(dynamic binding)。

\begin{itemize}
\item 静态联编(static binding)，又称为早期联编(early binding)
\item 动态联编(dynamic binding)：有虚函数的存在，编译时不知道会选择哪一个函数。
\end{itemize}

\subsection{指针和引用的兼容性}\label{指针和引用的兼容性}

通常，C++不允许将一种类型的地址赋值给另一种类型的指针，也不允许一种类型的引用指向另一种类型。

但是，对于继承来说，情况有所不同，前文已有叙述，即"基类指针或引用可以在不发生显示类型转换的情况下指向派生类对象"这一点。

\subsubsection{向上强制转换}

将派生类引用或指针转换为基类引用或指针，被称为\textbf{向上强制转换(upcasting)}。这使得公有继承不需要进行显式类型转换，该规则是is-a关系的一部分。

向上强制转换是可以传递的。假如类A1派生出了A2，A2又派生出了A3。则A1指针或者引用可以引用A1对象，A2对象，A3对象。

\subsubsection{向下强制转换}

将基类指针或者引用转换为派生类的指针或引用，被称为\textbf{向下强制转换(downcasting)}。如果不显式地进行类型转换，向下强制转换是\textbf{不允许的}。原因是，is-a关系通常是不可逆的。

\subsection{虚成员函数和动态联编}
\label{虚成员函数和动态联编}

前面已经指出，如果将方法声明为虚的，如果使用指针调用该方法，则在程序运行的时候，才能根据对象的类型来决定使用哪一种方法。即，编译器对虚方法使用动态联编。

那么，提出如下问题

\begin{enumerate}
\item 为什么有两种类型的联编。
\item 既然动态联编有这么灵活的特点，为什么不只保留动态联编。
\item 动态联编是如何工作的。
\end{enumerate}

对于前两个问题，其答案来自于C++的指导原则之一，即，不为不需要的特性付出代价(指内存空间和处理时间，即，空间和时间)。所以，编译器只会在一定需要使用动态联编的时候使用动态联编，而不是全局使用。

对于一些，不需要在派生类中重新定义的成员函数，则不应该将之设置为虚函数。这样避免使用动态联编，提高了效率。其次，指出那些方法不需要重新定义，也便于开展派生类的开发工作。

要回答下一个问题，请看下一节。

\subsection{虚函数的工作原理}
\label{虚函数的工作原理}
C++规定了虚函数的行为，但是并没有规定其实现方法，而是将其实现细节留给了编译器的作者。

\subsubsection{虚函数表}
每个类都会有一块存储所有方法的内存，如果调用其中的方法，则会跳转到此处开始运行。这块内存使用函数地址数组管理，被称为\textbf{虚函数表(virtual function table, vtbl)}。

虚函数表是属于类的，所有类的对象公用一个虚函数表。

\subsubsection{虚函数表指针}
类的每个对象都添加一个隐藏的成员，其中保存了一个指向刚才那个函数地址数组，即，指向函数地址数组的指针，可以称之为\textbf{虚函数表指针vptr}。注意，这里是指向数组的指针。

虚函数表指针是属于单独一个类对象的，每个类对象的虚函数表指针不一定相同(这就体现了多态)

\subsubsection{虚函数表的内容}
虚函数表是与类相关的，所有的对象共用一个虚函数表。每一个对象，都有一个虚函数表指针，指出它应该按照哪一个虚函数表来执行其接口。

虚函数表指针，指向一个虚函数表。虚函数表里的函数指针，指向代码段的某个函数。

基类对象的隐藏指针指向基类的虚函数表，派生类对象的隐藏指针，则指向派生类的虚函数表。同一个类的多个对象也都指向同一块内存，同一个虚函数表。

如果派生类提供了虚函数的新定义，则该虚函数表将保存新函数的地址，该函数的地址也将被添加到vtbl中。如果派生类没有重新定义虚函数，该vtbl将保存函数原始版本的地址。

调用虚函数时，系统根据虚函数表指针vptr，查看存储在对象中的vtbl地址，然后转向相应的虚函数表，查找相应的函数，然后跳转到对应的函数开头地址处。

\subsubsection{什么时候生成的虚函数表}
编译期就已生成了。遇到\verb|virtual|关键字时就会开始生成了。

\subsubsection{虚函数表存放在哪里}
\verb|.rodata|，只读数据段，虚函数表存放在这里

\verb|.bss|，未初始化的或者初始化为0的变量。\verb|.data|，已初始化的全局变量、静态变量。

代码段存放\verb|.text|和\verb|.rodata|，数据段存放\verb|.bss|和\verb|.data|

虚函数表，是函数指针数组，函数指针指向\verb|.text|里的函数

\subsubsection{什么时候生成的虚函数表指针}
在类对象构造的时候，就把类的虚函数表的地址，赋值给对象的虚函数表指针。如果没有给出构造函数，编译器会给出默认构造函数。

发生继承的情况下，先调用基类构造函数，把基类的虚函数表的地址赋值给vptr，再调用派生类构造函数，把派生类的虚函数表的地址，赋值给vptr，一共赋值了2次。

\subsubsection{虚函数表与拷贝}
虚函数表指针，与浅拷贝。一个对象释放了，如果是浅拷贝，也会把虚函数表给释放了，而影响其他对象里的虚函数表指针的正常使用。

有虚函数的类，建议是\textbf{显式定义拷贝构造函数}，并且\textbf{重载赋值运算符}，避免发生浅拷贝。

\subsubsection{使用虚函数的代价}
总之，使用虚函数时，都会在内存和时间上付出一定成本，主要体现在

\begin{enumerate}
\item 每个类对象都会增大，增大的量为存储地址所需的空间
\item 对于每个类，编译器都将创建一个虚函数地址表(数组)
\item 对于每个函数调用，都将执行一条额外的操作，即，到虚函数表中查询地址
\end{enumerate}

\section{抽象基类}
抽象基类(ABC, Abstract Base Class)

椭圆，圆，信息冗余

解决办法：将椭圆和圆放到一个ABC中，再从这个ABC中派生出椭圆和圆

椭圆类和圆类都有一个求面积的功能需要实现(假如函数命名为\verb|Area()|)，但是二者求面积需要的参数是不同的。那么如何借助ABC来实现\verb|Area()|呢？

\subsection{纯虚函数}
由于在定义ABC的时候，还未确定需要哪些参数，所以，虽然可以确定的是一定要有一个\verb|Area()|函数，但是它的实现当前在ABC中并不能给出。C++提供了\textbf{纯虚函数(pure virtual function)}来创建一个未实现的函数。

纯虚函数声明的结尾处为\verb|=0|

\begin{lstlisting}
virtual double Area() const = 0;
\end{lstlisting}

派生类在继承时，必须对抽象基类中的纯虚函数进行重写(overwrite)，否则编译器会报错。重写的时候，函数特征标必须保持一致，比如参数列表和\verb|const|特性等。

\subsubsection{抽象基类与具体类}
在原型中使用\verb|=0|使之成为一个\textbf{抽象基类}，在类中可以不定义该函数。。、

像之前提到的椭圆和圆这种类，有时候可以对应地称之为具体类(concrete class)

当类声明中包含纯虚函数时，不能创建该类的对象。抽象基类是不能实例化的。包含纯虚函数的类，只能被用作基类。

\subsubsection{纯虚函数也可以有定义}
纯虚函数是可以有定义的。在基类中，仍然可以给纯虚函数写一个定义。目的是提供框架式、未完整的实现(这都根据开发人员的需求来选择)，或者是给定一个默认的实现。

但是，尽管写了定义，在派生类中，也一定要对纯虚函数进行重写，否则编译器仍然会报错。

\subsubsection{ABC的理念}
抽象基类与纯虚函数，实际上指出了项目中有某些\textbf{必须给出实现的接口}，强制地要求其派生类覆盖掉纯虚函数，逼迫派生类遵循ABC设置的接口规则。

\section{继承与动态内存分配}
问题的前提是，基类使用了动态内存分配，并重新定义了赋值和拷贝构造函数。

\subsubsection{派生类不使用new}
\textbf{结论}：不需要重新定义显式析构函数，拷贝构造函数和赋值运算符。因为派生类的默认构造函数，默认拷贝构造函数和默认赋值运算符，都会对应地调用基类的。

\subsection{派生类使用new}
\textbf{结论}：必须为派生类重新定义显式析构函数，拷贝构造函数和赋值运算符。

\subsubsection{析构函数} 
派生类的析构函数，主要是将自己所管理的动态内存的创建和释放做好即可，基类的由基类的析构函数负责了。

\subsubsection{拷贝构造函数} 
\begin{lstlisting}
hasDMA::hasDMA(const hasDMA & hs) : baseDMA(hs)
{
    style = new char[std::strlen(hs.style) + 1];
    std::strcpy(style, hs.style);
}
\end{lstlisting}

\verb|baseDMA|拷贝构造函数使用\verb|hasDMA|参数的\verb|baseDMA|部分来构造新对象的\verb|baseDMA|部分。

\subsubsection{赋值运算符} 

显式地调用了基类赋值运算符

\begin{lstlisting}
hasDMA & hasDMA::operator=(const hasDMA & hs)
{
    if (this == &hs)
        return *this;
    baseDMA::operator=(hs);     // copy base portion
    delete [] style;            // prepare for new style
    style = new char[std::strlen(hs.style) + 1];
    std::strcpy(style, hs.style);
    return *this;
}
\end{lstlisting}

\subsubsection{小结}

\begin{enumerate}
\item 对于析构函数，是自动完成的，自动调用了基类的析构函数。只需要做好派生类的动态内存管理部分的工作即可。
\item 对于拷贝构造函数，通过成员初始化列表中调用基类的拷贝构造函数来完成的。如果不这样做，将自动调用基类的默认构造函数。
\item 对于赋值运算符，通过使用作用域解析运算符显式地调用基类的赋值运算符来完成的。
\end{enumerate}

\subsection{与友元}

基类\verb|baseDMA|，派生类\verb|lacksDMA|和\verb|hasDMA|

\verb|baseDMA|、\verb|lacksDMA|和\verb|hasDMA|都各自有友元函数如下(当然，这是各自写在各自的类声明里的)

\begin{lstlisting}
friend std::ostream & operator<<(std::ostream & os, const baseDMA & rs);
friend std::ostream & operator<<(std::ostream & os, const lacksDMA & rs);
friend std::ostream & operator<<(std::ostream & os, const hasDMA & rs);
\end{lstlisting}

那么出现问题了，作为\verb|hasDMA|类的友元，函数\verb|operator<<|能够访问派生类的成员，但是它能否访问基类的成员呢？因为它不是基类的友元(函数名虽然相同，但是函数原型不同，说明是不同的函数，不要混淆)

答案是要显式地使用基类的友元函数\verb|operator<<|。但是由于友元不是类的成员，所以不能使用作用域解析运算符来显式地指出要使用哪个函数，这个问题的解决办法是使用强制类型转换，将参数\verb|const hasDMA &|转换为类型\verb|const baseDMA &|。

\begin{lstlisting}
std::ostream & operator<<(std::ostream & os, const hasDMA & hs)
{
// type cast to match operator<<(ostream & , const baseDMA &)
    os << (const baseDMA &) hs;
    os << "Style: " << hs.style << endl;
    return os;
}
\end{lstlisting}

\section{代码重用}

\subsubsection{代码重用}

C++为了增强现代码重用而提供了多种技术，比如第\ref{公有继承}节介绍的公有继承就是其中一种。

\begin{itemize}
\item 包含(containment)、组合(composition)或者层次化(layering)(第\ref{包含(containment)}节)

一个类包含着另外一个类，这种技巧被称为包含(containment)、组合(composition)或者层次化(layering)

\item 私有继承或者保护继承
\item 类模板(第\ref{类模板}节)
\end{itemize}

\subsection{包含(containment)}\label{包含(containment)}

包含(containment)可以建立has-a关系，即，让一个类包含其他的类，让其他的类作为某个类的成员。

将其他的类声明为某个类的私有成员。在类的内部，成员方法可以通过私有成员的类的公有方法来访问私有成员里面的私有成员。但是类的外部，是无法使用私有成员的类的公有方法的，外部只能通过类的公有方法来访问。

对于has-a关系来说，类对象不能自动获得被包含对象的接口，因为外部是无法直接访问私有成员的，包括显式地调用私有成员的方法。而对于公有继承来说，类是直接继承接口的。

不继承接口，是has-a关系的特点之一。

\subsubsection{学生数据库的示例}

\begin{code_example}\label{学生数据库的示例}
\begin{lstlisting}
// studentc.h -- defining a Student class using containment
#ifndef STUDENTC_H_
#define STUDENTC_H_

#include <iostream>
#include <string>
#include <valarray>
class Student
{
private:
    typedef std::valarray<double> ArrayDb;
    std::string name; // contained object
    ArrayDb scores; // contained object
    // private method for scores output
    std::ostream & arr_out(std::ostream & os) const;
public:
    Student() : name("Null Student"), scores() {}
    explicit Student(const std::string & s) : name(s), scores() {}
    explicit Student(int n) : name("Nully"), scores(n) {}
    Student(const std::string & s, int n) : name(s), scores(n) {}
    Student(const std::string & s, const ArrayDb & a) : name(s), scores(a) {}
    Student(const char * str, const double * pd, int n) : name(str), scores(pd, n) {}
    ~Student() {}
    double Average() const;
    const std::string & Name() const;
    double & operator[](int i);
    double operator[](int i) const;
    
// friends
    // input
    friend std::istream & operator>>(std::istream & is, Student & stu); // 1 word
    friend std::istream & getline(std::istream & is, Student & stu); // 1 line
    // output
    friend std::ostream & operator<<(std::ostream & os, const Student & stu);
};
\end{lstlisting}
\end{code_example}

\paragraph{typedef的用法}

注意\verb|typedef std::valarray<double> ArrayDb;|这里的用法

它表示在\verb|Student|类的内部，可以用\verb|ArrayDb|代替\verb|std::valarray<double>|，而在类的外部是不可以的。

\paragraph{explicit的用法}

\begin{lstlisting}
explicit Student(const std::string & s)
    : name(s), scores() {}
explicit Student(int n) : name("Nully"), scores(n) {}
\end{lstlisting}

这里使用关键字\verb|explicit|，关闭了从\verb|int|到\verb|Student|的隐式类型转换。

由于也没有给出显式类型转换，等于说，禁止了从\verb|int|到\verb|Student|的类型转换。

这样，一旦程序中发生了这样的类型转换，则会在编译期就报错。

C++允许程序员限制程序的某些特性，比如使用关键字\verb|explicit|防止单参数构造函数的隐式转换，使用关键字\verb|const|限制方法修改数据等等。这样做的原因是，\textbf{在编译阶段出现错误，要优于在运行阶段出现错误}

\subsubsection{初始化被包含的对象}

\begin{lstlisting}
Student(const char * str, const double * pd, int n) : name(str), scores(pd, n) {}
\end{lstlisting}

\verb|name(str)|调用\verb|name|所属的类的构造函数，\verb|scores(pd, n)|调用\verb|scores|所属的类的构造函数

\paragraph{初始化的顺序} 初始化的顺序是成员被声明的顺序，而不是他们在初始化列表中的顺序。

\section{私有继承与保护继承}

私有继承也可以实现has-a关系。

私有继承使用关键字\verb|private|

\paragraph{派生类并不继承基类的接口} 使用私有继承，基类的公有成员和保护成员，都将成为派生类的私有成员。基类方法将不会暴露在派生对象公有接口中，但是可以在派生类的成员函数内部使用它们。即，派生类并不继承基类的接口。

\subsubsection{多重继承}

使用多个基类的继承被称为多重继承(multiple inheritance, MI)

一般来说，多重公有继承会带来许多问题，但是多重私有继承，产生的副作用较少。

\begin{lstlisting}
class Student : private std::string, private std::valarray<double>
{
public:
    ...
};
\end{lstlisting}

私有继承与包含的区别主要在于，被包含的类，前者是匿名的，后者是有命名的。那么， 私有继承如何初始化匿名成员呢？

\subsection{使用私有继承}

\subsubsection{初始化基类组件}

由于成员是匿名的，所以不能使用成员名，而应该使用对应的类名

\begin{lstlisting}
Student(const char * str, const double * pd, int n) : std::string(str), ArrayDb(pd, n) {}
\end{lstlisting}

\subsubsection{访问基类的方法}

在派生类的内部使用基类的方法，由于私有继承被包含的类是匿名的，所以也无法使用成员名来调用基类的方法，而应该使用基类的类名加作用域解析运算符的方式。

\begin{lstlisting}
double Student::Average() const
{
    if (ArrayDb::size() > 0)
        return ArrayDb::sum()/ArrayDb::size();
    else
        return 0;
}
\end{lstlisting}

\subsubsection{访问基类对象}

既然基类对象本身没有明确的命名，那应该如何访问基类对象呢？

答案是\textbf{使用强制类型转换}

将指针\verb|this|从指向派生类的指针转变为指向基类的指针

\begin{lstlisting}
const string & Student::Name() const
{
    return (const string &) *this;
}
\end{lstlisting}

\subsubsection{访问基类的友元函数}

同样使用类型转换的方式来调用想要调用的函数(基类或派生类)

\begin{lstlisting}
ostream & operator<<(ostream & os, const Student & stu)
{
    os << "Scores for " << (const String &) stu << ":\n";
    ...
}
\end{lstlisting}

\subsection{与包含的区别}

\subsubsection{与包含的区别} 包含(containment)将对象作为一个命名的成员添加在类中，而私有继承是将对象作为一个匿名的对象添加在类中。为了作区分，可以使用\textbf{子对象(subject)}来区分使用包含时被包含的类和使用私有继承时被放在类中的类。

\subsubsection{使用包含还是使用私有继承} 大多数C++程序员倾向于使用包含，因为它便于理解。

从多个基类进行多重继承，会带来很多难以解决的问题。

包含能够包括多个同类的子对象，而私有继承只能带有一个(因为是以匿名身份加入派生类的)

但是，私有继承，可以让派生类访问保护成员。但是包含之后的类，由于它不是派生类，它与被包含的类之间并不是继承关系，所以它并不能访问保护成员，它不在继承链中，它位于继承结构之外。

另一种需要使用私有继承的情况是，需要重新定义虚函数。派生类可以重新定义虚函数，而包含时并不能。

\subsection{保护继承}

保护继承是私有继承的一种变体，使用关键字\verb|protected|

使用保护继承时，基类的公有成员和保护成员，都将成为派生类的保护成员。

基类的接口在派生类中也是可用的，这一点与私有继承是一样的。

\subsubsection{与私有继承的区别} 使用私有继承时，第三代类将不能使用基类的接口，因为基类的公有方法在私有派生类中将变成私有方法。

而使用保护继承时，基类的公有方法在第二代类中将变成保护方法，因此在第三代派生类中可以使用它们。

\begin{table}[!ht]
\centering
\caption{各种继承方式} \label{各种继承方式}
\begin{tabular}{|l|l|l|l|}
\hline
特征 & 公有继承 & 保护继承 & 私有继承 \\ \hline
公有成员会变成 & 派生类的公有成员 & 派生类的保护成员 & 派生类的私有成员 \\ \hline
保护成员会变成 & 派生类的保护成员 & 派生类的保护成员 & 派生类的私有成员 \\ \hline
私有成员会变成 & 只能通过基类接口访问 & 只能通过基类接口访问 & 只能通过基类接口访问 \\ \hline
能否隐式向上转换 & 是 & 是(但只能在派生类中) & 否 \\ \hline
\end{tabular}
\end{table}

\subsubsection{使用using重新定义访问权限}

在使用私有派生或保护派生时，基类的公有成员将成为私有成员或者保护成员。

如何在类的外部也能使用基类的方法呢？

方法之一是，在派生类中定义一个公有的方法，在这个方法内部使用基类的方法。

另一种方法是，将函数调用包装在另一个函数调用中，即使用一个\verb|using|声明来指出派生类可以使用特定的基类成员。

\begin{lstlisting}
class Student : private std::string, private std::valarray<double>
{
...
public:
    using std::valarray<double>::min;
    using std::valarray<double>::max;
    ...
};
\end{lstlisting}

上述的\verb|using|声明使得两种基类方法在\verb|Student|的外部也可用，就像他们是\verb|Student|的公有方法一样

注意，\verb|using|声明只使用成员名，没有圆括号，函数特征标和返回类型。

\begin{lstlisting}
using std::valarray<double>::operator[];
\end{lstlisting}

\verb|using|声明只适用于继承，而不适用于包含。

\section{多重继承}

公有多重继承表示的也是is-a关系

私有MI和保护MI可以表示has-a关系

MI可能带来的问题

\begin{itemize}
\item 从2个不同的基类继承了同名方法。
\item 从多个基类那里继承同一个类的多个实例。
\end{itemize}

\subsection{虚基类(virtual base class)}
有多个类，他们都是从一个基类派生出来的，而从这多个类通过多重继承派生出的一个类，只继承一个基类对象。

对于上面的案例，可以将\verb|Worker|被用作\verb|Singer|和\verb|Waiter|的虚基类

关键字\verb|virtual|和\verb|public|的顺序无关紧要

\begin{lstlisting}
class Singer : virtual public Worker {...};
class Waiter : public virtual Worker {...};
\end{lstlisting}

\subsubsection{案例}

定义一个抽象基类\verb|Worker|，从它派生出\verb|Waiter|和\verb|Singer|，并从它们两个派生出\verb|SingerWaiter|

\verb|Singer|和\verb|Waiter|里各有一个基类\verb|Worker|，那么\verb|SingerWaiter|里面有几个基类呢？

\paragraph{二义性} 按照以前的写法，可以将派生类对象的地址赋给基类指针。但是在当前的情况下，这个基类指针，将指向\verb|Waiter|和\verb|Singer|中哪一个的\verb|Worker|呢？产生了二义性的问题。

虽然可以使用强制类型转换来表面地解决二义性的问题。但是这并没有触及问题的根本，即，\verb|SingerWaiter|类为什么在内部会有两个\verb|Worker|基类对象呢？其实是不需要的，因为\verb|Singer|和\verb|Waiter|都是\verb|Worker|的派生类，在\verb|SingerWaiter|里面只需要一个\verb|Worker|基类对象就可以了。

\begin{lstlisting}
    Worker * pw1 = (Waiter *) &ed; // the Worker in Waiter
    Worker * pw2 = (Singer *) &ed; // the Worker in Singer
\end{lstlisting}

为了解决这个问题，提出了虚基类的技术

\subsubsection{虚基类和虚函数的关系} 没有什么关系，C++标准在这里同样使用了重载的思想，即，对关键字\verb|virtual|进行了重载。

\subsubsection{为什么不默认所有多重继承的基类都是虚基类}

\begin{enumerate}
\item 在某些情况下，可能需要多个基类的拷贝。
\item 将基类作为虚基类，需要额外的开销(内存或运算时间)。而C++的设计理念是，不为了不需要的功能而付出代价。
\item 其他。
\end{enumerate}


\subsection{使用虚基类时的构造函数}

\subsubsection{非虚基类} 

对于非虚基类，假如A派生了B，B派生了C。C类的构造函数只能调用B类的构造函数，B类的构造函数只能调用A类的构造函数。C类的构造函数使用参数q，并把m和n传递给B类的构造函数，B类的构造函数只使用参数m，并把n传递给A类的构造函数

\begin{lstlisting}
class A
{
    int a;
public:
    A(int n = 0) : a(n) {}
    ...
};
class B: public A
{
    int b;
public:
    B(int m = 0, int n = 0) : A(n), b(m) {}
    ...
};
class C : public B
{
    int c;
public:
    C(int q = 0, int m = 0, int n = 0) : B(m, n), c(q) {}
    ...
};
\end{lstlisting}

\subsubsection{使用虚基类时}

对于虚基类，假如A是虚基类，这种信息自动传递，将不会起作用。例如下面这样的构造函数，由于有多重继承，则参数wk将通过两条途径传递给基类Worker对象的构造函数。

\begin{lstlisting}
SingingWaiter(const Worker & wk, int p = 0, int v = Singer::other)
            : Waiter(wk,p), Singer(wk,v) {} // flawed
\end{lstlisting}

为了避免这种冲突，C++在使用虚基类时，禁止信息通过中间类自动传递给基类。上面的代码，将会使用Worker的默认构造函数

如果不想使用Worker的默认构造函数来初始化它，则需要显式地调用所需的基类的构造函数。

\begin{lstlisting}
SingingWaiter(const Worker & wk, int p = 0, int v = Singer::other)
            : Worker(wk), Waiter(wk,p), Singer(wk,v) {}
\end{lstlisting}

这种用法，只有在使用虚基类的时候才是合法的，如果不是虚基类，则是错误的。

\subsection{方法成员的二义性}

多重继承可能导致方法调用的二义性。例如SingerWaiter可能从Singer和Waiter中继承了两个完全不同的同名方法。

可以使用作用域解析运算符来指出想要使用哪个方法。

\begin{lstlisting}
SingingWaiter newhire("Elise Hawks", 2005, 6, soprano);
newhire.Singer::Show(); // use Singer version
\end{lstlisting}

更好的方法是在SingerWaiter中重新定义一下该同名方法，并在这个方法的定义里指明要使用哪一个方法。

\begin{lstlisting}
void SingingWaiter::Show()
{
    Singer::Show();
}
\end{lstlisting}

但是，这么做的缺陷是，SingingWaiter中的Waiter部分，即，属于Waiter又不属于Worker的部分，不会在\verb|Show()|中被显示到。如果在函数体中加上\verb|Waiter::show()|呢？那Worker部分又会被\verb|show()|两次。

\subsubsection{解决方式}
使用模块化的方式，将数据成员分割成，仅仅属于Worker的，属于Waiter而不属于Worker的，属于Singer而不属于Worker的，这三部分，然后组合成\verb|show()|函数的定义。

\subsection{其他注意事项}

\subsubsection{混合使用虚基类和非虚基类} 不管有多少个虚途径，虚基类的子对象只有一个。而非虚基类的子对象，与非虚途径的个数相同。

\subsubsection{虚基类和支配} 

如果某个名称，优先于(donimates)其他所有名称，则使用它时，即使不使用限定符，也不会导致二义性。

那么这个\textbf{优先于}的含义是什么呢？如何决定一个标识符优先于另一个标识符呢？简单来说，派生类中的名称，优先于直接或者间接祖先中相同的名称。

\section{类模板}\label{类模板}

\subsection{什么是类模板}

类模板也是代码重用的方式之一

比如容器类，它除了存储的数据类型不同之外，其他操作基本是相同的，这里就可以使用类模板，便于快速地生成不同的容器类。

我们用\textbf{泛型}这个词，来描述与类型无关的情况

模板提供参数化(parameterized)类型，即，能够接受一种类型，来传递给接收方来生成相应的类及其方法成员函数。

\subsection{使用类模板}

\subsubsection{如何定义类模板}

类模板的开头，一般以下面这样的代码组成。这里的\verb|Type|代表一种数据类型，而不代表某个字面值，这表示，类模板接收一个数据类型作为参数。这一行中的\verb|class|也可以替换为\verb|typename|，较新的C++代码中更多地使用后者。

\verb|Type|也成为泛型标识符，也成为类型参数(type parameter)，赋值给他们的不能是值，而必须是类型。

\begin{lstlisting}
template <class Type>
template <typename Type> // newer choice
\end{lstlisting}

模板成员函数也应该有相同的代码开头\verb|template <class Type>|。同样，应该用泛型名\verb|Type|来代替原来具体的类名，具体的写法如下

\begin{lstlisting}
template <class Type> // or template <typename Type>
bool Stack<Type>::push(const Type & item)
{
    ...
}
\end{lstlisting}

如果在类声明中定义了方法，即，使用了内联方法，则可以省略模板前缀和类限定符。

\paragraph{类模板的代码位置} 不能将模板成员函数放在独立的实现文件中，由于模板不是具体的函数，他们不能单独编译，模板必须与特定的模板实例化请求一起使用。因此，必须将整个模板的所有信息(声明与定义)放在同一个头文件中。

以前的C++标准提供了关键字\verb|export|，允许模板成员函数放在独立的实现文件中，但是C++11已经取消了这个关键字的这个用法。但是并未删除该关键字，而是保留了它在其他地方的用法。

\subsubsection{如何使用类模板}

与函数模板类似，仅仅写了类模板的实现，并不会真的让编译器生成某个具体的类，而是必须要进行请求实例化的过程，才会生成对应的具体的类。

类模板要求必须显式地给出所需的类型，而不能像函数模板一样传入一个字面值而由函数模板来自动获取类型。

\subsubsection{将指针用作类型参数使用类模板}

C++ Primer Plus (6th edition)原书第572页第14.4.3节

前文已经讨论了，将内置类型或类对象用作类模板\verb|Stack<Type>|的类型，类模板可以实例化一个类。那么，这里的\verb|Type|换做指针可以吗？

换言之，就是这里的\verb|Type|是否对于所有的数据类型都适用？是否有不适用的情况？不适用的话会发生什么错误？C++又有什么补救措施？

原书第572-573页，描述了几种将之前的\verb|Stack<std::string>|改造成\verb|Stack<char*>|的尝试，分析了其可能产生的效果和失败的原因。

正确使用指针栈的方法之一，是让调用程序提供一个指针数组，其中，每个指针都指向不同的字符串。这样，利用指针栈来存储这些指针，是有实际意义的。

注意，创建不同指针是调用程序的职责，而非栈的职责，栈的任务是管理指针，而不是创建指针。

示例代码(程序清单14.16)，字符串本身是不移动的，所谓"把字符串压入栈"实际上是新建一个指向该字符串的指针，该指针的值时现有字符串的地址。

"从栈弹出字符串"就是把地址值复制到另外的地方。

\subsubsection{非类型参数}

C++ Primer Plus (6th edition)原书第577页第14.4.4节

模板经常被用来创建容器类，这是因为类型参数的概念非常适合于将相同的存储方案用于不同的数据类型。

下面将探讨一些非类型(或表达式)参数以及如何使用数组来处理继承族。

如何创建一个\textbf{允许指定数组大小的数组模板}。

\begin{enumerate}
    \item 在类中使用动态数组和构造函数参数来提供元素数目
    \item 使用模板参数来提供常规数组的大小。C++11新增的模板\verb|array|就是采用的这种方法。
\end{enumerate}

\begin{lstlisting}
template <class T, int n>
class ArrayTP
{
    private:
    T ar[n];
    public:
    ArrayTP(){};
    explicit ArrayTp(const T & v);
    virtual T & operator[](int i);
    virtual T operator[](int i) const;
};
\end{lstlisting}

\paragraph{程序说明}

模板头\verb|template <class T, int n>|

这里面的\verb|int n|被称为非类型(non-type)或表达式(expression)参数

假设有这样的声明，\verb|ArrayTP<double, 12> eggweights;|。则编译器将定义一个名为\verb|ArrayTP<double, 12>|的类，并创建一个类型为\verb|ArrayTP<double, 12>|的\verb|eggweights|对象。使用\verb|double|替换\verb|T|，使用\verb|12|替换\verb|n|

\paragraph{非类型参数的限制}

非类型参数可以是整型、枚举、引用或者指针。

模板代码不能修改参数的值，也不能使用参数的地址。所以，在模板中不能使用诸如\verb|n++|和\verb|&n|等表达式。

实例化模板时，用作非类型参数的值，必须是常量表达式。

\paragraph{非类型参数的优缺点}

\begin{itemize}
\item \textbf{优点}："使用动态数组和构造函数参数"的方法，是通过\verb|new|和\verb|delete|来管理的堆的内存。而非类型参数方法，使用的是为自动变量维护的栈的内存。所以执行速度更快，尤其是使用大量小数组的时候。
\item \textbf{缺点}：每一种数组大小都会生成自己的模板。
\end{itemize}

\subsection{模板多功能性}

C++ Primer Plus (6th edition)原书第578页第14.4.5节

可以将常规类的一些技术，应用于模板类。

模板类可以用作基类，也可以用作组件类，还可以用作其他模板的类型参数(例如可以用数组模板实现栈模板)。

C++98要求使用至少一个空白字符将两个\verb|>|符号分开，以免与运算符\verb|>>|混淆。C++11已经不要求这么做了。

\subsubsection{递归使用模板}

\verb|ArrayTP<ArratTP<int, 5>, 10> twodee;|

这样就创建了一个数组\verb|twodee|，它有10个元素，每个元素是一个包含5个int元素的数组。

与之等价的常规数组的声明是\verb|int twodee[10][5]|

请注意表示维度的顺序(数字5和10的前后顺序)，二者是相反的。

\subsubsection{使用多个类型参数}

比如STL中提供的\verb|pair|容器

\subsubsection{默认类型模板参数}

可以为类型参数提供默认值

\verb|template <class T1, class T2 = int> class Tpop {...};|

可以为类模板类型参数提供默认值，但是不能为函数模板参数提供默认值。

但是对于非类型参数，类模板和函数模板都是可以提供默认值的。

\subsection{模板的具体化}

C++ Primer Plus (6th edition)原书第582页第14.4.6节

\begin{itemize}
\item 隐式实例化
\item 显式实例化
\item 显式具体化
\end{itemize}

\subsubsection{隐式实例化}

\verb|ArrayTP<int, 100> stuff;|

\subsubsection{显式实例化}

当使用关键字\verb|template|并指出所需类型来声明类时，编译器将生成类声明的显式实例化(explicit instantiation)。

例如，下面的声明将\verb|ArrayTP<string, 100>|声明为一个类。

\verb|template class ArrayTP<string, 100>;|

虽然并没有创建类对象，但是编译器仍然会生成对应的类的声明。

和隐式实例化一样，也将根据通用模板来生成具体化。

\subsubsection{显式具体化}

显式具体化指的是，需要为特殊类型实例化时，对模板进行修改，使其行为不同。

具体化类模板定义的格式如下。(早期的编译器，可能无须加上\verb|template <>|)

\verb|template <> class Classname<specialized-type-name> {...};|

例如，要使用新的表示法提供一个专供\verb|const char *|类型使用的\verb|SortedArray|模板，代码如下

\begin{lstlisting}
template <> class SortedArray<const char *>
{
    ...  // details
};
\end{lstlisting}

\subsubsection{部分具体化}

\textbf{部分具体化(partial specialization)}，即部分限制模板的通用性。

部分具体化可以给类型参数之一指定具体的类型

\begin{lstlisting}
// general template
template <class T1, class T2> class Pair {...};
// specialization with T2 set to int
template <class T1> class Pair<T1, int> {...};
\end{lstlisting}

关键字\verb|template|后面的\verb|<>|声明的是没有被具体化的类型参数。因此，上述第二个声明将\verb|T2|具体化为\verb|int|，但是\verb|T1|保持不变。

注意，如果指定所有的类型，则\verb|<>|里面应该是空的，这就成了显式具体化。

\subsubsection{类模板的优先级}

如果有多个模板可供选择，编译器将使用具体化程度最高的模板。

\subsection{成员模板}

C++ Primer Plus (6th edition)原书第584页第14.4.7节

模板可以用作结构、类或模板类的成员。这项特性的适用场景之一，就是实现STL中各种与类型无关的容器和算法等功能。

\begin{lstlisting}
template <typename T>
class beta
{
private:
    template <typename V>  // nested template class member
    class hold
    {
    private:
        V val;
    public:
        hold (V v = 0) : Val(v) {}
        void show() const { std::cout << val << std::endl; }
        V Value() const { return val; }
    };
    hold<T> q;  // template object
    hold<int> n;  // template object
public:
    beta(T t, int i) : q(t), n(i) {}
    template<typename U>  // template method
    U blab(U u, T t) { return (n.Value() + q.Value()) * u / t; }
    void Show() const { q.Show(); n.Show(); }
};

int main()
{
    beta<double> guy(3.5, 3);
    cout << "T was set to double\n";
    guy.Show();
    cout << "V was set to T, which is double, then V was set to int\n";
    cout << guy.blab(10, 2.3) << endl;
    cout << "U was set to int\n";
    cout << guy.blab(10.0, 2.3) << endl;
    cout << "U was set to double\n";
    cout << "Done\n";
    return 0;
}
\end{lstlisting}

在上面的代码示例中，分别实现了\verb|beta|类模板和\verb|hold|类模板。\verb|hold|模板时在\verb|beta|模板的私有部分声明的，因此就只能在\verb|beta|类中访问它。

\verb|beta|类模板有2个私有成员\verb|q|和\verb|n|，这两个成员都使用\verb|hold|模板声明的。其中，\verb|n|是基于\verb|int|类型的\verb|hold|对象，\verb|q|是基于\verb|T|类型(\verb|beta|模板参数)的\verb|hold|对象。

\subsubsection{构造函数}

那么我们该如何声明一个这样使用了成员模板的模板类对象呢？(如何使用类模板的构造函数？)

\begin{lstlisting}
beta<double> guy(3.5, 3);
\end{lstlisting}

其中类型参数\verb|double|被传递给了成员模板\verb|beta|的类型参数\verb|T|，所以其私有成员\verb|q|的类型为\verb|hold<double>|

\subsubsection{方法成员函数模板}

\begin{lstlisting}
template<typename U>
U blab(U u, T t) { return (n.Value() + q.Value()) * u / t; }
\end{lstlisting}

方法成员函数模板\verb|blab()|有两个模板参数\verb|U|和\verb|T|，其中\verb|T|由对象实例化时接受到的类型参数所决定。类型\verb|U|由该方法被调用时参数值显式地决定。

可以看代码示例中的其中2行

\begin{lstlisting}
cout << guy.blab(10, 2.3) << endl;
cout << guy.blab(10.0, 2.3) << endl;
\end{lstlisting}

第一行中将类型\verb|U|指定为\verb|int|

第二行中将类型\verb|U|指定为\verb|double|

根据前面的代码，类型参数\verb|T|是\verb|double|，而它也同样传递给了方法模板\verb|template<typename U> U blab(U u, T t)|的第2个参数。那么，在运行\verb|guy.blab(10, 3)|的时候，会发生将3转换为\verb|double|(即类型参数\verb|T|)的情况。

\subsubsection{在类模板之外定义模板方法}

\begin{lstlisting}
template <typename T>
class beta
{
private:
    template <typename V> // declaration
    class hold;
    hold<T> q;
    hold<int> n;
public:
    beta( T t, int i) : q(t), n(i) {}
    template<typename U> // declaration
    U blab(U u, T t);
    void Show() const { q.show(); n.show();}
};

// member definition
template <typename T>
    template<typename V>
        class beta<T>::hold
        {
        private:
            V val;
        public:
            hold(V v = 0) : val(v) {}
            void show() const { std::cout << val << std::endl; }
            V Value() const { return val; }
        };
    
// member definition
template <typename T>
    template <typename U>
        U beta<T>::blab(U u, T t)
        {
            return (n.Value() + q.Value()) * u / t;
        }
\end{lstlisting}

在上面的代码示例中，在类模板\verb|template <typename T>class beta|中，只是声明了\verb|template <typename V> class hold|，但是并没有给出其定义。然后在类模板的声明之外，给出了成员模板的定义和方法模板的定义，注意留意它的写法。

\verb|hold|和\verb|blab()|是类模板\verb|beta<T>|的成员，注意在这里使用作用域解析运算符

\subsection{将模板用作参数}

C++ Primer Plus (6th edition)原书第586页第14.4.8节

模板除了可以包含类型参数和非类型参数，还可以包含本身就是模板的参数，这是用于实现STL的重要特性。

\begin{code_example}\label{将模板用作参数}
\begin{lstlisting}
#include <iostream>
#include "stacktp.h"

template <template <typename T> class Thing>
class Crab
{
private:
    Thing<int> s1;
    Thing<double> s2;
public:
    Crab() {};
    // assumes the thing class has push() and pop() members
    bool push(int a, double x) { return s1.push(a) && s2.push(x); }
    bool pop(int & a, double & x){ return s1.pop(a) && s2.pop(x); }
};

int main()
{
    using std::cout;
    using std::cin;
    using std::endl;
    
    Crab<Stack> nebula;
// Stack must match template <typename T> class thing

    int ni;
    double nb;
    cout << "Enter int double pairs, such as 4 3.5 (0 0 to end):\n";
    while (cin>> ni >> nb && ni > 0 && nb > 0)
    {
        if (!nebula.push(ni, nb))
            break;
    }
    while (nebula.pop(ni, nb))
        cout << ni << ", " << nb << endl;
    cout << "Done.\n";
    return 0;
}
\end{lstlisting}
\end{code_example}

\subsubsection{什么是模板参数}

如果说\verb|template <typename T> class beta|中，模板参数是\verb|T|

那么对于\verb|template <template <typename T> class Thing> class Crab|中，

模板参数是\verb|template <typename T> class Thing|，

其中，\verb|template <typename T> class|是类型，\verb|Thing|是参数，也叫做模板参数(与类型参数相对比)

\subsubsection{传递模板参数}

类模板\verb|Crab|有两个私有成员\verb|Thing<int> s1|和\verb|Thing<double> s2|

代码示例中有\verb|Crab<Stack> nebula;|

如果\verb|Stack|是满足\verb|template <typename T> class Thing|的模板

则上面的代码，将会把\verb|Stack|传递给\verb|Thing|

(要牢记，\verb|Stack|这里是模板，而不是类型、字面值或者别的东西)

所以运行了代码\verb|Crab<Stack> nebula;|之后

\verb|nebula|的私有成员\verb|s1|将会被实例化为\verb|Stack<int>|

\verb|s2|将会被实例化为\verb|Stack<double>|

\subsubsection{模板参数的深层含义}

代码示例中，\verb|Crab|类模板，对于\verb|Thing|为代表的模板类做了另外3个假设，即，这些类包含\verb|push()|和\verb|pop()|方法，而且这些方法有特定的接口。换句话说，\verb|Crab|类可以使用任何与\verb|Thing|类型匹配，并且包含\verb|push()|和\verb|pop()|方法的模板类。

\subsubsection{混合使用模板参数和常规参数(即，类型参数)}

\begin{code_example}\label{混合使用模板参数和常规参数}
\begin{lstlisting}
template <template <typename T> class Thing, typename U, typename V>
class Crab
{
private:
    Thing<U> s1;
    Thing<V> s2;
...
}
\end{lstlisting}
\end{code_example}

现在，\verb|Crab|类模板里的\verb|s1|和\verb|s2|成员的类型，并不是固定的了，而是为一个泛型，在实例化的时候传入才会生成相应的类的代码(声明与定义)。

\verb|T|表示模板参数，\verb|U|和\verb|V|表示非模板的类型参数。

使用方式如下。

\begin{lstlisting}
Crab<Stack, int, double> nebula; // T=Stack, U=int, V=double
\end{lstlisting}

\subsection{模板类和友元}

C++ Primer Plus (6th edition)原书第588页第14.4.9节

模板类声明也可以有友元，模板的友元可以分为3类

\begin{enumerate}
\item 非模板友元
\item 约束(bound)模板友元，即友元的类型取决于类被实例化时的类型
\item 非约束(unbound)模板友元，即友元的所有具体化都是类的每一个具体化的友元
\end{enumerate}

\subsubsection{非模板友元函数(non-template friend functions)}

在模板类中将一个普通的函数，声明为其友元，使其成为该模板的所有实例化的友元。不管模板的类型参数\verb|T|传入的是什么类型，他们都可以具有这个友元。

\begin{code_example}
\begin{lstlisting}
template <class T>
class HasFriend
{
public:
    friend void counts(); // friend to all HasFriend instantiations
    ...
};
\end{lstlisting}
\end{code_example}

\paragraph{为友元函数提供模板类参数}

在下面的代码示例中，就可以将模板的类型参数\verb|T|传递给友元函数。

\begin{code_example}
\begin{lstlisting}
template <class T>
class HasFriend
{
    friend void report(HasFriend<T> &); // bound template friend
    ...
};
\end{lstlisting}
\end{code_example}

\paragraph{为友元函数显式具体化}

用到一种类型，就要为了，以这个类型为类型参数的模板类的友元函数，提供一个显式的初始化。

\begin{lstlisting}
void report(HasFriend<short> &) {...}; // explicit specialization for short
void report(HasFriend<int> &) {...}; // explicit specialization for int
\end{lstlisting}

\paragraph{缺陷}

这本身就不够灵活，因为使用模板的出发点之一，是让程序员可以不去关注具体有哪些类型。而当前这种友元函数的使用方式，让程序员必须关注这个类模板用了哪些类型，并逐个为这些类型显式具体化它们所需要的友元函数。

所以下面就给出了一种改进的做法。

\subsubsection{约束模板友元函数(bound template friend functions)}

解决思路就是，让友元函数本身也成为模板。使得，模板类的每一个具体化，都对应地获得与之匹配的模板友元函数的具体化。

即，你用什么类型来具体化一个模板类，系统自动用这个类型，来具体化它所需要的友元函数(前提是，我们将友元函数改造成了模板函数，或者更细致的说法是，约束模板友元函数。而不再是像上一节中那样的普通函数。)

\paragraph{使用方法}

主要有3个步骤

\begin{enumerate}
\item 在类定义前，声明每个模板函数

\begin{lstlisting}
template <typename T> void counts();
template <typename T> void report(T &);
\end{lstlisting}

\item 在函数中再次将模板声明为友元

\begin{lstlisting}
template <typename TT>
class HasFriendT
{
...
    friend void counts<TT>();
    friend void report<>(HasFriendT<TT> &);
};
\end{lstlisting}

其中\verb|report()|也可以写作

\begin{lstlisting}
report<HasFriendT<TT> >(HasFriendT<TT> &)
\end{lstlisting}

假如模板实例化出了这样一个对象\verb|HasFriendT<int> squack;|，则编译器会自动生成如下的类定义代码。

\begin{lstlisting}
class HasFriendT<int>
{
    ...
    friend void counts<int>();
    friend void report<>(HasFriendT<int> &);
};
\end{lstlisting}

\item 为友元提供模板定义
\end{enumerate}

可以看代码示例\ref{约束模板友元函数}，在其主函数中，\verb|count()|函数被调用了两次，但是每次使用了不同的类型参数。

为什么\verb|count()|调用时需要使用类型参数，而\verb|report()|调用时不需要使用类型参数？

因为\verb|count()|自身是无法知道需要使用哪种类型参数来具体化的，但是\verb|report()|的函数原型中有一个\verb|T &|，它能知道用哪个类型来具体化。

\begin{code_example}\label{约束模板友元函数}
\begin{lstlisting}
// tmp2tmp.cpp -- template friends to a template class
#include <iostream>
using std::cout;
using std::endl;

// template prototypes
template <typename T> void counts();
template <typename T> void report(T &);

// template class
template <typename TT>
class HasFriendT
{
    private:
    TT item;
    static int ct;
    public:
    HasFriendT(const TT & i) : item(i) {ct++;}
    ~HasFriendT() { ct--; }
    friend void counts<TT>();
    friend void report<>(HasFriendT<TT> &);
};

template <typename T>
int HasFriendT<T>::ct = 0;

// template friend functions definitions
template <typename T>
void counts()
{
    cout << "template size: " << sizeof(HasFriendT<T>) << "; ";
    cout << "template counts(): " << HasFriendT<T>::ct << endl;
}

template <typename T>
void report(T & hf)
{
    cout << hf.item << endl;
}

int main()
{
    counts<int>();
    HasFriendT<int> hfi1(10);
    HasFriendT<int> hfi2(20);
    HasFriendT<double> hfdb(10.5);
    report(hfi1); // generate report(HasFriendT<int> &)
    report(hfi2); // generate report(HasFriendT<int> &)
    report(hfdb); // generate report(HasFriendT<double> &)
    cout << "counts<int>() output:\n";
    counts<int>();
    cout << "counts<double>() output:\n";
    counts<double>();
    return 0;
}
\end{lstlisting}
\end{code_example}

\subsubsection{非约束模板友元函数(unbound template friend functions)}

前一节，约束模板友元函数，是在类外面声明的模板的具体化。

而非约束模板友元函数，是指，在类内部声明模板，使得，每个函数具体化都是每个类具体化的友元。

对于非约束友元，友元模板类型参数与模板类类型参数是不同的。

\begin{lstlisting}
template <typename T>
class ManyFriend
{
...
    template <typename C, typename D> friend void show2(C &, D &);
};
\end{lstlisting}

在代码示例\ref{非约束模板友元函数}中，调用\verb|show2(hfi1, hfi2)|时所对应的具体化是下面的

\begin{lstlisting}
void show2<ManyFriend<int> &, ManyFriend<int> &>
            (ManyFriend<int> & c, ManyFriend<int> & d);
\end{lstlisting}

调用\verb|show2(hfdb, hfi2)|时所对应的具体化是下面的

\begin{lstlisting}
void show2<ManyFriend<double> &, ManyFriend<int> &>
            (ManyFriend<double> & c, ManyFriend<int> & d);
\end{lstlisting}

\begin{code_example}\label{非约束模板友元函数}
\begin{lstlisting}
// manyfrnd.cpp -- unbound template friend to a template class
#include <iostream>
using std::cout;
using std::endl;

template <typename T>
class ManyFriend
{
private:
    T item;
public:
    ManyFriend(const T & i) : item(i) {}
    template <typename C, typename D> friend void show2(C &, D &);
};

template <typename C, typename D> void show2(C & c, D & d)
{
    cout << c.item << ", " << d.item << endl;
}

int main()
{
    ManyFriend<int> hfi1(10);
    ManyFriend<int> hfi2(20);
    ManyFriend<double> hfdb(10.5);
    cout << "hfi1, hfi2: ";
    show2(hfi1, hfi2);
    cout << "hfdb, hfi2: ";
    show2(hfdb, hfi2);
    
    return 0;
}
\end{lstlisting}
\end{code_example}

\subsection{模板别名}\label{模板别名}

C++ Primer Plus (6th edition)原书第593页第14.4.10节

\subsubsection{模板具体化的别名}

可使用\verb|typedef|为模板具体化指定一个别名。

\begin{lstlisting}
typedef std::array<double, 12> arrd;
typedef std::array<int, 12> arri;
arrd gallons;  // gallons is type std::array<double, 12>
arri days;  // days is type std::array<int, 12>
\end{lstlisting}

\subsubsection{(C++11)模板的别名}
C++11新增了一项功能，可以给模板提供别名。
\begin{lstlisting}
template<typename T>
using arrtype = std::array<T, 12>;  // template to create multiple aliases
\end{lstlisting}

可以如下这样使用上面创建的模板别名\verb|arrtype|，\verb|arrtype<T>|表示类型\verb|std::array<T, 12>|

\begin{lstlisting}
arrtype<double> gallons;  // gallons is type std::array<double, 12>
arrtype<int> days;  // days is type std::array<int, 12>
\end{lstlisting}

\subsubsection{将using=用于非模板}

C++11允许将语法\verb|using=|用于非模板的情况。用于非模板时，这种语法与常规\verb|typedef|是等价的。

\subsubsection{可变参数模板}

C++11新增的另一项模板功能是\textbf{可变参数模板(variadic template)}，可以定义一种，可接受可变数量的参数的，模板类和模板函数。

\section{再谈面向对象}
\subsection{面向对象的三大特征}
\subsubsection{封装}
封装的目的，可以隐藏细节，为模块化做准备。

控制访问权限。public，private，protected

\subsubsection{继承}
无须修改原有类，扩展新的功能

权限继承

公有继承

保护继承，基类在派生类中的最高权限只能到protected，原来的公有成员，都变成protected成员。

私有继承

如何破坏继承关系？friend友元，using语句。

多重继承，菱形继承。如何解决菱形继承的问题？用虚基类

\subsubsection{多态}
目的是，一个接口，多种形态。通过接口重用，增强可扩展性。

静态多态，函数重载

动态多态，虚函数重写


\part{模板元编程}
\chapter{泛型编程}
\section{类型萃取}
\verb|type_traits| 在 C++中是非常有用的技巧，可以说如果不懂 \verb|type_traits|，那根本看不懂 stl 相关代码，最近对 \verb|type_traits| 比较感兴趣，于是找到了 gcc 的 \verb|type_traits| 源码通读了一遍，总结一下。
\verb|type_traits| 称为类型萃取技术，主要用于编译期获取某一参数、某一变量、某一个类等等任何 C++相关对象的类型，以及判断他们是否是某个类型，两个变量是否是同一类型，是否是标量、是否是引用、是否是指针、是左值还是右值，还可以将某个为某个类型去掉 cv(const volitale)属性或者增加 cv 属性等等。

通过\verb|type_traits|可以实现在编译期计算、查询、判断、转换和选择，增强了泛型编程的能力，也增强了程序的弹性，使得我们在编译期就能做到优化改进甚至排错，能进一步提高代码质量。

C++20 提供了 \verb|Concept| 的特性，使用 \verb|Concept| 可以使得实现类似的功能更加方便。

\subsection{std::integral\_constant}
\verb|type_traits| 最核心的结构体应该就是 \verb|std::integral_constant|

\verb|std::integral_constant|是一个类模板，用于为特定类型封装一个静态常量和对应类型，是整个c++ type traits的基础。
首先我们看模板参数，有两个参数，第一个是类型\verb|_Tp|，第二个是对应类型的值\verb|__v|，\verb|value|是一个编译期就能确定的常量，值根据\verb|__v|确定，\verb|value_type|就是类型\verb|_Tp|。

有两个成员函数，均用于返回wrapped value，都是\verb|constexpr|，可以获得编译期的值：
\begin{enumerate}
    \item 类型转换函数方式：\verb|constexpr operator value_type() const noexcept;|
    \item 仿函数方式：\verb|constexpr value_type operator()() const noexcept; (since C++14)|
\end{enumerate}

\begin{lstlisting}
  template<typename _Tp, _Tp __v>
struct integral_constant
{
    static constexpr _Tp                  value = __v;
    typedef _Tp                           value_type;
    typedef integral_constant<_Tp, __v>   type;
    constexpr operator value_type() const noexcept { return value; }
    constexpr value_type operator()() const noexcept { return value; }
};

typedef integral_constant<bool, true>     true_type;
typedef integral_constant<bool, false>    false_type;
\end{lstlisting}

\verb|operator type() const| 用于类型转换，而 \verb|type operator()() const| 用于仿函数。可以看下面的代码示例。

\begin{lstlisting}
#include <iostream>
#include <type_traits>

using std::cout;
using std::endl;

class Test
{
    public:
    operator int() const
    {
        cout << "operator type const " << endl;
        return 1;
    }
    
    int operator()() const
    {
        cout << "operator()()" << endl;
        return 2;
    }
};

int main()
{
    Test t;
    int x(t);
    int xx = t;
    t();
    return 0;
}

// 编译运行：g++ test.cc; ./a.out
// operator type const
// operator type const
// operator()()
\end{lstlisting}

\subsection{模板std::conditional}
当模板的第一个参数为 \verb|true| 时 \verb|type| 就是第二个参数的类型，当第一个参数为 \verb|false| 时 \verb|type| 就是第三个参数的类型，通过 \verb|std::conditional| 可以构造出 \verb|or|和\verb|and| 等功能
\begin{lstlisting}
 template<bool, typename, typename>
struct conditional;

template<bool _Cond, typename _Iftrue, typename _Iffalse>
struct conditional
{ typedef _Iftrue type; };

// Partial specialization for false.
template<typename _Iftrue, typename _Iffalse>
struct conditional<false, _Iftrue, _Iffalse>
{ typedef _Iffalse type; };
\end{lstlisting}

\section{名称查找与ADL(Argument-Dependent Lookup)}
在 C++中，如果编译器遇到一个名称，它会寻找这个名称代表什么。比如 \verb|x * y|，如果 x 和 y 是变量的名称，那么就是乘法。如果 x 是一个类型的名称，那么就声明了一个指针。

C++是一个context-sensitive的语言 : 必须知道上下文才能知道表达式的意义。那么这个和模板的关系是什么呢？构造一个模板必须知道几个上下文
\begin{enumerate}
    \item 模板出现的上下文
    \item 模板被实例化的上下文
    \item 实例化模板参数的上下文
\end{enumerate}

\subsection{名称的分类}
\begin{enumerate}
    \item qualified name: 如果一个名称所属的作用域被显式的指明，例如\verb|::|，\verb|->|或者圆点\verb|.|，比如\verb|this->count|就是一个 qualified name
    \item dependent name: 依赖于模板参数的名称，也就是访问运算符左面的表达式类型依赖于模板参数。比如\verb|std::vector<T>::iterator|。假如这里的\verb|T|是一个已知类型的别名，比如\verb|using T = int|，那就不是 Dependent Name。
\end{enumerate}

\subsection{名称查找}
\subsubsection{常规的查找情景，ordinary lookup}
对于 qualified name 来说，会有显示指明的作用域。如果作用域是一个类，那么基类也会被考虑在内，但是类的外围作用域不会被考虑。这点很符合直觉。

对于非 qualified name 来说，会在外围作用域逐层查找(假如在类成员函数中，会先找本类和基类的作用域)。

在下面的代码示例中，有3个\verb|count|。
\begin{lstlisting}
extern int count;             // #1

int lookup_example(int count) // #2
{
    if (count < 0) {
        int count = 1;           // #3
        lookup_example(count);   // unqualified count refers to #3
    }
    return count + ::count;      // the first (unqualified) count refers to #2 ;
}                              // the second (qualified) count refers to #1
\end{lstlisting}

但是下面的例子，就比较复杂。\verb|::max|语句，并不会查找到\verb|BigMath::BigNumber|

ADL 就是这个特殊规则，用来解决此类的问题。
\begin{lstlisting}
template<typename T>
T max (T a, T b) {
    return b < a ? a : b;
}

namespace BigMath {
    class BigNumber {
        ...
    };
    
    bool operator < (BigNumber const&, BigNumber const&);
    ...
}

using BigMath::BigNumber;

void g (BigNumber const& a, BigNumber const& b) {
    ...
    BigNumber x = ::max(a,b);
    ...
}
\end{lstlisting}

\subsubsection{ADL}
ADL最早出现在 C++98/C++03 中，也叫"依赖于参数的查询"，也被叫做Koenig查找(Koenig lookup)，应用在非 qualified name的查找上，下文简称 unqualified name。

简单来说：如果在函数的名称空间中定义了一种或多种参数类型，则不必为函数限定名称空间。

ADL 会将函数表达式中实参的associated namespaces和associated classes加入到查找范围。例如：某一类型是指向 class X 的指针，那么它的associated namespaces和associated classes会包含 X 和 X 所属的任何 class 和 namespace。

ADL 会忽略 using

在下面的代码示例中，既没有使用using声明也没有使用using指令，但是编译器仍然可以通过应用Koenig查找将正确的不合格名称\verb|doSomething()|识别为在命名空间\verb|MyNamespace|中声明的函数。

在上面的代码中，编译器发现对象\verb|obj|(它是函数\verb|doSomething()|的参数)属于命名空间\verb|MyNamespace|。因此，它将查看该命名空间以找到\verb|doSomething()|的声明。
\begin{lstlisting}
namespace MyNamespace
{
    class MyClass {};
    void doSomething(MyClass);
}

MyNamespace::MyClass obj; // global object


int main()
{
    doSomething(obj); // Works Fine - MyNamespace::doSomething() is called.
}
\end{lstlisting}

\subsubsection{ADL的缺陷}
依赖 ADL 有可能会导致语义问题，这也是为什么有的时候需要在函数前面加\verb|::|，或者一般推荐使用 \verb|xxx::func|，而不是 \verb|using namespace xxx| 。因为前者是 qualified name，没有 ADL 的过程。









\chapter{标准模板库STL}

C++ Primer Plus (6th edition)原书第674页，第16.3节

STL最早发布于1994年。

STL不是面向对象的变成，而是一种不同的编程模式，即，泛型编程(generic programming)

\section{以vector为例子引出STL思想}

各种STL容器模板都接受一个可选的模板参数，该参数指定使用哪个分配器对象来管理内存。

例如，\verb|vector|模板的开头类似于

\begin{lstlisting}
template <class T, class Allocator = allocator<T> >
class vector { ...
\end{lstlisting}

如果省略该模板参数的值，则容器模板将默认使用\verb|allocator<T>|类，这个类使用\verb|new|和\verb|delete|

\subsection{初始化vector}
\paragraph{使用花括号}
\begin{lstlisting}
std::vector<int> myVector = { 1,2,3,4,5 };
\end{lstlisting}

\paragraph{仅指定个数}
遵循的构造函数原型是(C++11)
\begin{lstlisting}
explicit vector( size_type count );
\end{lstlisting}

\paragraph{指定元素和个数}
\begin{lstlisting}
std::vector<int> myVector(5, 100);
\end{lstlisting}

遵循的构造函数原型是(C++11)
\begin{lstlisting}
vector( size_type count, const T& value,
        const Allocator& alloc = Allocator() );  
\end{lstlisting}

\paragraph{使用迭代器}
遵循的构造函数原型是(C++11)
\begin{lstlisting}
template< class InputIt >
vector( InputIt first, InputIt last, const Allocator& alloc = Allocator() );
\end{lstlisting}

举例
\begin{lstlisting}
int arr[5] = { 1,2,3,4,5 };
vector<int> myVector = { 1,2,3,4 };

//输出内容是：1 2 3
vector<int> vector1(arr, arr + 3);
\end{lstlisting}

\paragraph{使用传统C数组}
\verb|std::vector|具备接受迭代器范围的构造函数，你可以把C数组的起始指针和结束指针当作迭代器传入。
\begin{lstlisting}
int cArray[] = {1, 2, 3, 4, 5};
size_t arraySize = sizeof(cArray) / sizeof(cArray[0]);
std::vector<int> vec(cArray, cArray + arraySize);
\end{lstlisting}

\subsection{基本操作}
\paragraph{判断非空}
判断容器是否为空
\begin{lstlisting}
bool empty() const
\end{lstlisting}

\paragraph{查询元素个数}
返回向量中元素的个数
\begin{lstlisting}
int size() const
\end{lstlisting}

\paragraph{查询容器容量}
返回当前向量所能容纳的最大元素值
\begin{lstlisting}
int capacity() const
\end{lstlisting}

\paragraph{查询容器最大容量}
\verb|int max_size() const|返回最大可允许的vector元素数量值

\paragraph{扩大所占据的内存空间}
\verb|reserve()|的作用是更改vector的容量(capacity)，使vector至少可以容纳n个元素。

如果n大于vector当前的容量，reserve会对vector进行扩容。其他情况下都不会重新分配vector的存储空间。
\begin{lstlisting}
void reserve (size_type n);
\end{lstlisting}

\verb|resize(size_type count)|将容器的大小调整为给定的值，如果增加了容器的空间，多出来的元素会被以默认构造函数来补位。相比之下，\verb|reserve()|只是更改占据的空间，并没有填充任何元素。

\subsection{迭代器}
\paragraph{含义}
迭代器是一种广义的指针。他有的时候可以是指针，有的时候是一个类对象，只不过行为上与指针极其类似(比如解除引用\verb|operator*()|，或者递增\verb|operator++()|)。他具体是如何实现的，这要根据模板的具体实现而灵活调整。

为\verb|vector|的\verb|double|类型声明一个迭代器

\begin{lstlisting}
vector<double>::iterator pd;
\end{lstlisting}

\paragraph{使用迭代器}
\begin{lstlisting}
vector<double> scores;

pd = scores.begin();  // 指向第一个元素
*pd = 22.3;  // 解除引用，并给其赋值
++pd;  // 指向下一个元素
\end{lstlisting}

\verb|begin()|成员指向第一个元素的迭代器。

使用C++11自动类型推断，还可以这样写
\begin{lstlisting}
auto pd = scores.begin();
\end{lstlisting}

\paragraph{超尾(past-the-end)}
超尾指的是，迭代器指向容器最后一个元素后面的位置。

\verb|end()|成员，返回超尾位置。

遍历整个容器的写法
\begin{lstlisting}
for (pd = scores.begin(); pd != scores.end(); pd++)
{
    statements;
}
\end{lstlisting}

\paragraph{迭代器需要满足的条件}
我们在这里，简单总结一下迭代器需要满足的条件(必要条件)，迭代器一定满足这些功能，但是满足这些功能不一定就是迭代器。
\begin{enumerate}
    \item 应该定义解除引用的操作，即\verb|*p|，以便于让他访问他所指向的值。因为迭代器是一种广义指针，他要能够指向容器中的所有元素。
    \item 应该定义将一个迭代器赋值给另一个迭代器的操作，即\verb|p = q|
    \item 应该定义两个迭代器之间进行比较的操作，即\verb|p == q|或者\verb|p != q|
    \item 应该定义累加操作，即\verb|p++|或者\verb|++p|
\end{enumerate}

\subsection{元素获取与增删}
\paragraph{访问vector中的元素}
\verb|front()|，返回第一个元素的引用

\verb|back()|，返回最后一个元素的引用

\verb|data()|，返回指向第一个元素的指针

\paragraph{operator[]和at()的区别}
都返回引用，所以既可以读也可以写。

对于下标越界，二者处理方式不同。\verb|operator[]|对此的处理是未定义行为UB，但是不会崩溃。但是\verb|at()|会报错，抛出\verb|std::out_of_range|异常。
对于\verb|std::map|，对于non-const版本，如果访问了不存在的key，\verb|at()|会抛出\verb|std::out_of_range|异常，但是使用\verb|operator[]|会隐式创建这个元素，value默认为0。如果是\verb|const std::map|，则不能使用\verb|operator[]|，因为\verb|operator[]|是一个non-const函数，只能使用\verb|at()|
\begin{lstlisting}
std::cout << m.count(‘x’) ? m.at(‘x’) : -1;  // 两次queries
auto it = m.find(‘x’); std::cout << it != m.end() ? it->second : -1;  // 一次query
\end{lstlisting}

(a?b:c表达式，如果a为true则返回b，a为false则返回c)

\paragraph{增加元素}
\verb|push_back()|方法，在容器的最后一个位置添加一个元素。

\verb|insert()|方法.

\verb|iterator insert(iterator it, const T& x)|，在迭代器指向的位置的前面增加元素。

\verb|iterator insert(iterator it, int n, const T& x)|，在迭代器指向的位置的前面增加n哥元素。

\verb|iterator insert(iterator it, const_iterator first, const_iterator last)|，在迭代器指向的位置的前面，把第2个和第3个参数迭代器所划定的范围的元素插入。

可以看下面的代码示例

\begin{lstlisting}
vector<int> old_v;
vector<int> new_v;
...
old_v.insert(old_v.begin(), new_v.begin()+1, new_v.end());
\end{lstlisting}

\paragraph{push\_back和emplace\_back的区别与联系}
C++11还推出了\verb|emplace_back()|，功能与\verb|push_back()|类似，但是效率更高，细节上更完善。

对于\verb|std::map|来说

\begin{lstlisting}
    my_map.insert({1, “hello”});  // OK
    my_map.emplace(1, “hello”);  // 省去了花括号
\end{lstlisting}

从上面的代码示例可以看出，\verb|emplace_back()|是支持多个构造参数的，而\verb|push_back()|是不支持的，只支持传入1个参数。

\verb|emplace_back()|是可以避免发生拷贝构造的，直接在容器内构造变量，省去了构造临时变量再赋值到容器里的过程。

\paragraph{交换元素}
\verb|void swap(vector&)|交换两个同类型向量的数据

\paragraph{设置元素}
\verb|void assign(int n,const T& x)|设置向量中前n个元素的值为x

\verb|void assign(const_iterator first,const_iterator last)|向量中[first,last)中元素设置成当前向量元素

\paragraph{内存空间管理}
与vector所占的内存空间有关的函数，有\verb|capacity()|，\verb|resize()|和\verb|reserve()|。

\verb|reserve()|可以提前分配vector的空间。

在不断向vector中添加元素时，vector预留的内存空间，会随着添加的元素个数增加而增加，增加的规律是2, 4, 8, 16, 32, 64, 128, 256, ...这样的顺序进行。

重新分配内存，并不会在原有的地址之后紧跟着分配的新的空间，一般会重新开辟一段更大的空间，再将原来的数据和新的数据放入新的空间。

\paragraph{删除元素}
\verb|erase()|方法，删除容器中给定区间的元素。它接收两个迭代器参数，这些参数定义了想要删除的区间，左开右闭。

\verb|iterator erase(iterator it)|，删除向量中迭代器指向元素

\verb|iterator erase(iterator first, iterator last)|，删除向量中\verb|[first,last)|中元素

\verb|void pop_back()|，删除向量中最后一个元素

\verb|void clear()|，清空向量中所有元素

\subsection{遍历}
\paragraph{for\_each()}
\verb|for_each()|函数接受3个参数，前两个是定义容器中区间的迭代器，最后一个是指向函数的指针(准确的说，是一个函数对象)

\verb|for_each()|函数可以用于任何容器类。

\verb|for_each()|函数将被指向的函数，应用于容器区间中的每个元素。被指向的函数，不能修改容器元素的值。

可以将下面的代码

\begin{lstlisting}
vector<Review>::iterator pr;
for (pr = books.begin(); pr != books.end(); pr++)
ShowReview(*pr);
\end{lstlisting}

替换为

\begin{lstlisting}
for_each(books.begin(), books.end(), ShowReview());
\end{lstlisting}

这样可以避免显式地使用迭代器变量。

\subsection{排序}

C++ Primer Plus (6th edition)原书第680页，第16.3.3节

\verb|vector|模板本身是否包含如搜索、排序等操作？并没有。

STL从更广泛的角度，定义了非成员(non-member)函数来执行这些操作，即，并不是为每一个容器类详细定义这些操作的成员函数，而是定义了一个适用于所有容器类的非成员函数，来针对所有的容器类都能执行这些操作。也就是说，这些函数并不是\verb|vector|自身的成员函数，而是通用的函数，适用于其他的STL容器。

\paragraph{random\_shuffle()}
头文件\verb|<algorithm>|

\verb|random_shuffle(begin, end)|函数，接受两个指定区间的迭代器参数，并随机排列该区间中的元素。这个函数要求容器类支持随机访问。

\paragraph{sort()}
头文件\verb|<algorithm>|

\verb|sort()|函数，要求容器类支持随机访问。

\verb|sort()|函数的基本使用方式是\verb|sort(begin, end, cmp)|，前两个参数是迭代器，第三个参数是一个函数对象，用于比较两个元素。参数\verb|cmp|的默认参数，是由小到大进行排序。如果想要由大到小排序可以用\verb|greater<int>()|

也可以自己定义一个函数来传递给参数\verb|cmp|，比如下面的代码示例

\begin{lstlisting}
bool cmp(int x,int y) {
    return x % 10 > y % 10;
}
\end{lstlisting}

比如，如果我们想要根据结构体中的某个成员，来对结构体进行排序，那么可以这样编写一个函数并传递给参数\verb|cmp|

\begin{lstlisting}
bool cmp_score(Student x,Student y) {
    return x.score > y.score;
}
\end{lstlisting}

\section{STL版本}
C++STL的版本实现有很多种，包括：HP STL、SGI STL、STL Port、P.J.Plauger STL和Rouge Wave STL等。

HP STL是Alexandar Stepanov在惠普Palo Alto实验室工作时，与Meng Lee合作完成的。HP STL是C++ STL的第一个实现版本，而且开放源代码。

SGI STL由Silicon Graphics Computer Systems公司参照HP STL实现，主要设计者仍然是STL之父Alexandar Stepanov。被Linux的C++编译器GCC所采用。

STL Port为了使SGI STL的基本代码都适用于VC++和C++ Builder等多种编译器，俄国人Boris Fomitchev建立了一个free项目来开发STLPort。跨平台适用于VC++、C++ Builder、GCC等。  可以与BOOST配合使用，实现跨平台代码。也是开源的。

被Visual C++编译器所采用的P.J.Plauger STL，和用于Borland C++编译器中的Rouge Wave STL。但是此二者都不是开源的。

\section{迭代器}
\subsection{为何要使用迭代器}
C++ Primer Plus (6th edition)原书第685页，第16.4.1节

理解迭代器是理解STL的关键。

\begin{itemize}
    \item 使用模板技术，可以使得算法独立于数据类型。(即，算法可以适配于所有数据类型，或者多种数据类型)
    \item 使用迭代器，可以使得算法独立于容器类型。算法可以适配于所有容器类型，或者多种容器类型。
\end{itemize}

这就是STL的"通用性"的原因之一。

\subsubsection{为两种不同数据结构实现find功能}

为double数组，编写一个搜索给定值的函数。如果函数在数组中找到符合条件的值，则返回该值在数组中的地址，否则返回一个空地址。该函数使用下标来遍历数组。

为链表，编写同样的搜索给定值的函数(见下面的代码示例)。

对链表的遍历，是通过\verb|start = start->p_next|来实现的。

\begin{lstlisting}
struct Node
{
    double item;
    Node * p_next;
};

Node * find_ll(Node * head, const double & val)
{
    Node * start;
    for (start = head; start != 0; start = start->p_next)
    {
        if (start->item == val)
        return start;
    }
}
\end{lstlisting}

可见，对与两种不同的数据结构(数组和链表)，比较符合条件的数值的过程是相似的，但是遍历的方式是不同的(数组下标，和指针指向下一个节点)

从泛型编程的思路出发，希望使用同一个find函数来处理数组和链表，甚至是其他的容器类型。即，函数不仅独立于容器中存储的数据类型，而且独立于容器本身的结构。

因此，形成了\textbf{迭代器}的设计思想。

\subsubsection{迭代器应当具有哪些特征}
迭代器应当具有哪些特征呢？我们做如下的总结

\begin{enumerate}
    \item 能够对迭代器执行解除引用的操作，以便能够访问它引用的值。即，如果\verb|p|是一个迭代器，则应该对\verb|*p|进行定义
    \item 应该能够将一个迭代器赋值给另一个，即对表达式\verb|p = q|进行定义。
    \item 应该能够将一个迭代器与另一个迭代器进行比较，看他们是否相等，即对表达式\verb|p == q|和\verb|p != q|进行定义。
    \item 应该能够使用迭代器遍历容器中的所有元素，这可以通过定义\verb|p++|或者\verb|++p|来实现。
\end{enumerate}

至少对于我们目前想要实现的find函数，上述的对迭代器的要求，暂时够用了。

STL通过对每个类定义适当的迭代器，并以统一的风格设计类，能够对内部表示截然不同的容器，编写相同的代码。

作为一种编程风格，最好避免直接使用迭代器，而应尽可能使用STL函数，如\verb|for_each()|来处理细节。

\subsubsection{迭代器类型}
C++ Primer Plus (6th edition)原书第688页，第16.4.2节

不同的算法对迭代器的要求也不同。

\begin{itemize}
    \item 查找算法，需要定义\verb|++|，因为要遍历
    \item 排序算法，需要能够随机访问，需要定义类似\verb|p+5|这样的运算。
\end{itemize}

STL定义了5种迭代器

\begin{enumerate}
    \item 输入迭代器
    \item 输出迭代器
    \item 正向迭代器
    \item 双向迭代器
    \item 随机访问迭代器
\end{enumerate}

\subsection{输入迭代器}
\subsubsection{简介}
这里所说的"输入"是从程序的角度说的，即，来自容器的信息被视为输入，即，某个容器给程序"输入"了信息。因此，输入迭代器可被程序用来读取容器中的信息(对输入迭代器使用解除引用运算符\verb|*|将使得程序能够读取容器中的值)。

输入迭代器可以访问容器中所有的值，依靠\verb|++|运算符(前缀后缀模式均可)来实现的。

输入迭代器在第二次遍历容器时，并不能保证容器的顺序与上一次的遍历的相同(因为上一次遍历很有可能改动了顺序)。输入迭代器被递增之后，也不能保证其先前的值仍然可以被解除引用。这就引出了"单通行"的概念。

\textbf{单通行的(single-pass)}，指的是，不依赖于前一次遍历时迭代器的值，也不依赖于本次遍历中前面的迭代器的值。迭代器自身是"独立于"上面提到的这两个的，它是"无记忆的"。

\subsubsection{std::istream\_iterator}
输入迭代器的常见例子就是\verb|std::istream_iterator|

原型
\begin{lstlisting}
template< class T,
        class CharT = char,
        class Traits = std::char_traits<CharT>,
        class Distance = std::ptrdiff_t >
class istream_iterator : public std::iterator<std::input_iterator_tag, 
                                                T, 
                                                Distance, 
                                                const T*, 
                                                const T&>  // (until C++17)
\end{lstlisting}

类型\verb|T|必须带有默认构造、拷贝构造和拷贝复制函数。

\subsubsection{参数说明}
\begin{itemize}
    \item \verb|class T|表示迭代器指向的元素的类型。例如，如果你想要从输入流中读取整数，那么\verb|T|就是\verb|int|。
    \item \verb|class Traits = std::char_traits<CharT>|从输入流中传入的字符的字符类型，默认值是\verb|char|，可选其他类型比如\verb|wchar_t|。
    \item \verb|class Traits = std::char_traits<CharT>|：这是字符类型的特征类。它定义了一系列与字符相关的操作，如字符的比较、复制等。默认值是\verb|std::char_traits<CharT>|。
    \item \verb|class Distance = std::ptrdiff_t|：这是迭代器之间的距离类型，用于表示两个迭代器之间的差值。默认值是\verb|std::ptrdiff_t|。
\end{itemize}

\subsubsection{表示行尾的迭代器}
如果在定义\verb|istream_iterator|时不为它指定\verb|istream|对象，它便代表了EOF(end-of-file)。

\subsubsection{示例：从标准输入流中读取两个值}
代码示例\ref{示例：从标准输入流中读取两个值}中，输入迭代器\verb|iit|在构造函数中传入了标准输入流\verb|std::cin|，说明是接收从标准输入流输入的数据。

\begin{code_example}\label{示例：从标准输入流中读取两个值}
\begin{lstlisting}
#include <iostream>
#include <iterator>

int main()
{
    double value1, value2;
    std::cout << "Please, insert two values: ";
    
    
    std::istream_iterator<double> iit(std::cin);    // stdin iterator
    std::istream_iterator<double> eos;              // end-of-stream iterator
    
    if (iit != eos) value1 = *iit;
    ++iit;
    if (iit != eos) value2 = *iit;
    
    
    std::cout << value1 << "*" << value2 << "=" << (value1*value2) << '\n';
    
    return 0;
}
\end{lstlisting}
\end{code_example}

代码输出
\begin{lstlisting}
Please, insert two values: 12 16
12*16=192
\end{lstlisting}

\subsubsection{示例：读取csv文件}
用C++读取目录下的123.txt文件内容，文件内容为
\begin{lstlisting}
023,456,789,012,345,678
234,567,890,123,456,789
345,678,901,234,567,890
\end{lstlisting}

在代码示例\ref{示例：读取csv文件}中，看\verb|copy(is,eof,back_inserter(text));|这一行。\verb|std::copy()|的第一和第二个参数，是想要输入数据的迭代器。这里是\verb|is|和\verb|eof|，表示的是前面从文件中读取到的数据(开头与超尾)。

第三个参数\verb|back_inserter|，他的含义是，将前面的数据，逐个存放到\verb|text|容器的末尾，准确的说，是\verb|text|容器的\verb|back_insert_iterator|迭代器中。而这个迭代器负责通过\verb|push_back()|接口来将该数据存放在\verb|text|容器的末尾。(有关\verb|back_insert_iterator|的内容请参考第\ref{插入迭代器}节)

\begin{code_example}\label{示例：读取csv文件}
\begin{lstlisting}
#include <iostream>
#include <fstream>
#include <iterator>
#include <algorithm>
#include <vector>
#include <string>
using namespace std;

int main()
{
    ifstream in_file("input_file.txt");
    ofstream out_file("output_file.txt");
    if(!in_file || !out_file)
    {
        cout<<"filesopen failed!\n";
        return -1;
    }
    istream_iterator<string> is(in_file);  // 自动按照空格分隔
    istream_iterator<string> eof;
    vector<string> text;
    
    copy(is,eof,back_inserter(text));
    sort(text.begin(),text.end());
    
    ostream_iterator<string> os(out_file," ");
    copy(text.begin(),text.end(),os);
    return 0;
}    
\end{lstlisting}
\end{code_example}

\subsection{输出迭代器}

与输入迭代器类似，这里的"输出"，指的是将信息从程序传输给容器。

对输出迭代器使用解除引用运算符\verb|*|，能够让程序可以修改容器的值，而并非读取。

"只能够写，不能都读"这件事其实并不稀奇，我们之前就遇到过，比如\verb|std::cout|，它就只能修改发送到终端上的流，但是却不能读取这个流。

对于单通行、只读算法，可以使用输入迭代器，对于单通行、只写算法，则可以使用输出迭代器。

小结一下，即，对于单通行、只读算法，可以使用输入迭代器，对于单通行、只写算法，可以使用输出迭代器。

\subsection{正向迭代器}

正向迭代器只能使用++运算符来遍历容器。

与输入、输出迭代器不同的是，正向迭代器总是按相同的顺序遍历一系列值。将正向迭代器递增后，仍然可以对前面的迭代器使用解除引用(如果保存了它的话)，并可以得到相同的值。这就使得原来的"单通行"变成为\textbf{多次通行}

\subsection{双向迭代器}

双向迭代器具有正向迭代器的所有特征，同时支持两种(前缀和后缀)递减运算符

\subsection{随机访问迭代器}

有些算法(如标准排序和二分检索)要求能够直接跳到容器中的任何一个元素，这叫做\textbf{随机访问}。

随机访问迭代器具有双向迭代器的所有特性，同时添加了支持随机访问的操作。

\begin{table}[htbp]\centering
    \caption{随机访问迭代器的操作} \label{随机访问迭代器的操作}
    \begin{tabular}{cl}
        \hline 表达式 & 描述 \\ \hline
        \verb|a + n|或\verb|n + a| & 指向a所指向的元素后的第n个元素 \\
        \verb|a - n| & 指向a所指向的元素前的第n个元素 \\
        \verb|r += n| & 等价于\verb|r = r + n| \\
        \verb|r -= n| & 等价于\verb|r = r - n| \\
        \verb|a[n]| & 等价于\verb|*(a + n)| \\
        \verb|b - a| & 结果为满足\verb|b = a + n|的\verb|n|的数值 \\
        \verb|a < b| & 如果\verb|b - a > 0|，则结果为\verb|true| \\
        \verb|a > b| & 如果\verb|b < a|，则结果为\verb|true| \\
        \verb|a >= b| & 如果\verb|!(a < b)|，则结果为\verb|true| \\
        \verb|a <= b| & 如果\verb|!(a > b)|，则结果为\verb|true| \\
        \hline
    \end{tabular}
\end{table}

像\verb|a + n|这样的表达式，当且仅当a和a + n都在容器区间(包括超尾)内时才合法。

\subsection{迭代器层次结构}

迭代器类型形成了一个层次结构。

\begin{itemize}
    \item 正向迭代器具有输入迭代器和输出迭代器的全部功能，同时还具有自己独有的功能。
    \item 双向迭代器具有正向迭代器的全部功能，同时还有自己独有的功能。
    \item 随机访问迭代器具有正向迭代器的全部功能，同时还有自己独有的功能。
\end{itemize}

为何要有这么多种类的迭代器呢？目的是为了在编写算法的时候尽可能使用最低要求的迭代器种类。

每个容器类都定义了一个类级别的\verb|typedef|名称\verb|iterator|，例如\verb|vector<int>|类的迭代器类型为\verb|vector<int>::iterator|。

\subsection{常量迭代器}
\subsubsection{const\_iterator}
\verb|const_iterator|和常量指针差不多，能读取但不能修改它所指的元素值。相反，\verb|iterator|的对象可读可写。

如果\verb|vector|对象或\verb|string|对象是一个常量，只能使用\verb|const_iterator|；如果\verb|vector|对象或\verb|string|对象不是常量，那么既能使用\verb|iterator|也能使用\verb|const_iterator|。
\begin{lstlisting}
// it能读写vector<int>的元素
vector<int>::iterator it;
// it2能读写string对象中的字符
vector<string>::iterator it2;
// it3 只能读元素,不能写元素
vector<int>::const_iterator it3;
// it4 只能读字符，不能写字符
vector<string>::const_iterator it4;
\end{lstlisting}

\subsubsection{类型推导}
通过类型推导占位符\verb|auto|，自动将\verb|const|容器的迭代器推导为\verb|const_iterator|。
\begin{lstlisting}
vector<int> v;
const vector<int> cv;
// vit1的类型是vector<int>::iterator
auto vit1 = v.begin();
// vit2的类型是vector<int>::const_iterator
auto vit2 = cv.begin();
//通过cbegin和cend可以获取常量迭代器
// cvit 类型为vector<int>::const_iterator
auto cvit = v.cbegin();
\end{lstlisting}

\subsection{插入迭代器}\label{插入迭代器}
插入迭代器有3种，\verb|back_inserter,inserter,front_inserter|

\section{概念、改进和模型}

我们前面提到的"正向迭代器"，实际上只是一系列要求，目前STL的正向迭代器可以满足这种要求，但是常规指针也能满足这种要求。

STL文献使用术语\textbf{概念(concept)}来描述这一系列的要求。

概念可以具有类似继承的关系。例如，双向迭代器继承了正向迭代器的功能。

从概念上看，它确实能够继承，有些STL文献使用术语\textbf{改进(refinement)}来表示这种概念上的继承，并不与C++的继承严格相同，只是二者有类似之处。

概念的具体实现被称为\textbf{模型(model)}。举个例子，指向\verb|int|的常规指针，是一个随机访问迭代器模型，也是一个正向迭代器模型，因为它也刚好满足"正向迭代器"概念的所有要求。

\subsection{将指针用作迭代器}

\subsubsection{sort()}

迭代器是广义的指针，而指针满足所有迭代器的要求。迭代器是STL算法的接口，而指针是迭代器。因此STL算法可以使用指针来对基于指针的非STL容器进行操作。

假设有这样的一个\verb|double|数组，并要按照升序对其进行排序。\verb|sort()|函数接受指向容器的第一个元素的迭代器和指向超尾的迭代器作为参数。而对于数组来说，\verb|&Receipts[0]|或者\verb|Receipts|是第一个元素的地址，\verb|&Receipts[SIZE]|或者\verb|Receipts + SIZE|是数组最后一个元素后面的元素的地址。

\begin{lstlisting}
    const int SIZE = 100;
    double Receipts[SIZE];
    
    sort(Receipts, Receipts + SIZE);
\end{lstlisting}

\subsubsection{copy()}

\verb|copy()|可以将数据从一个容器复制到另一个容器中。例如，下面的代码可以将一个数组复制到一个矢量中。\verb|copy()|的前两个迭代器参数表示要复制的范围，最后一个迭代器的参数表示要将第一个元素复制到什么位置。

不能使用\verb|copy()|将数据放到空矢量中(从某种角度来说，可以，但是需要使用一些技巧)。

\begin{lstlisting}
    int casts[10] = {6, 7, 2, 9, 4, 11, 8, 7, 10, 5};
    vector<int> dice[10];
    copy(casts, casts + 10, dice.begin());
\end{lstlisting}

\subsubsection{将信息赋值到显示屏上}

假设要将信息输出(复制)到显示屏上。如果有一个表示输出流的迭代器，则可以使用\verb|copy()|。

STL为这种迭代器提供了\verb|ostream_iterator|模板。用STL的话说，该模板是输出迭代器概念的一个模型，也可以说，它是一个\textbf{适配器(adaptor)}，指的是一个类或者函数，可以将一些其他接口转换为STL使用的接口。

\begin{lstlisting}
#include <iterator>
...
ostream_iterator<int, char> out_iter(cout, " ");
\end{lstlisting}

\verb|out_iter|是一个接口。第一个参数(\verb|int|)指出了被发送给输出流的数据类型，第二个模板参数(\verb|char|)指出了输出流使用的字符类型，这里也可以写成\verb|wchar_t|。构造函数的第一个参数(\verb|cout|)指出了要使用的输出流，最后一个字符串参数是在发送给输出流的每个数据项后显示的分隔符delimiter

可以这样使用迭代器

\begin{lstlisting}
    *out_iter++ = 15;  // works like cout << 15 << " ";
\end{lstlisting}

将容器\verb|dice|的整个区间复制到输出流中，可以这样写。或者直接使用匿名迭代器

\begin{lstlisting}
copy(dice.begin(), dice.end(), out_iter);
copy(dice.begin(), dice.end(), ostream_iterator<int, char>(cout, " ") );
\end{lstlisting}

\verb|istream_iterator|也使用两个模板参数，与\verb|ostream_iterator|相似。

\subsection{其他有用的迭代器}

\begin{itemize}
    \item \verb|reverse_iterator|
    \item \verb|back_insert_iterator|
    \item \verb|front_insert_iterator|
    \item \verb|insert_iterator|
\end{itemize}

\section{容器}

\subsection{容器种类}

\textbf{容器概念}: 概念是具有名称的通用类别

\textbf{容器类型}: 可以用于创建具体容器对象的模板

\subsubsection{容器概念}

类型必须是可拷贝构造的和可赋值的。基本类型满足这些要求，对于类，其定义中没有将赋值构造函数和赋值运算符声明为私有或保护的，则也满足这种要求。C++11改进了这些概念，添加了术语\textbf{可复制插入(Copy-Insertable)}和\textbf{可移动插入(Move-Insertable)}

基本容器不能保证其元素都按照特定的顺序存储，也不能保证元素的顺序不变，但对概念进行改造后，则可以增加这样的保证。

\paragraph{复杂度}

\textbf{复杂度}，描述了执行操作所需的时间，存在3个高低等级，编译时间，固定时间，线性时间。

\begin{itemize}
    \item 编译时间，操作将会在编译时执行，执行时间为0。
    \item 固定时间，意味着操作虽然发生在运行阶段，但是与容器中的元素数目无关。
    \item 线性时间，意味着与容器中的元素数目有关，成正比。如果a和b都是容器，则a==b具有线性复杂度，因为==运算必须用于元素中每一个元素。
\end{itemize}

复杂度要求是STL的特征之一，虽然实现细节可以隐藏，但是性能规格应当予以公开，以便程序员按需使用。

\subsubsection{C++11新增的容器要求}

拷贝构造和拷贝赋值以及移动构造和移动赋值之间的差别在于，复制操作保留源对象，而移动操作可修改源对象，还可能转让所有权，而不做任何复制。如果源对象是临时的，移动操作的效率将高于常规复制。(参见移动语义)。

\subsection{序列容器(sequence)}

序列(sequence)指的是一系列有序的容器类型。序列概念增加了"迭代器至少是正向迭代器"这一要求。

序列要求其内部的元素按照严格的线性顺序排列，存在第一个元素，最后一个元素(某些容器是没有"第一个"，"最后一个"这样的概念的)。
因为是有顺序的，所以支持将值插入到特定的位置、删除特定区间内的值等操作。

\subsubsection{线性表(数组) vector与array}

\verb|std::vector|需要头文件\verb|vector|，\verb|std::array|需要头文件\verb|array|

\verb|std::array|与传统的C数组是类似的，他的长度一经声明则无法改变。而\verb|std::vector|的长度是可以变化的。

\paragraph{std::array与传统数组的区别与联系}

内置的数组有很多麻烦的地方，比如无法直接对象赋值，无法直接拷贝等等，同时内置的数组又有很多比较难理解的地方，比如数组名是数组的起始地址等等。相比较于如\verb|std::vector|等容器的操作，内置数组确实有一些不方便的地方。因此，C++11就引入\verb|std::array|容器来代替内置数组。

引入\verb|std::array|，则可以将STL中具有普遍性的函数用到传统数组上。

\paragraph{复杂度分析}

\verb|std::vector|在尾部增删元素的时间是固定的，但是在除此之外的位置增删元素的复杂度是线性的。

是一种\textbf{可反转容器(reversible container)}，增加了\verb|rbegin()|和\verb|rend()|两个方法

\subsubsection{队列容器 deque, queue, priority\_queue}

队列满足先进先出FIFO规律(first-in, first-out)

\paragraph{单向队列queue}

元素从队列的尾部添加，从队列的头部退出队列。

初始化的方式，可以见下面的代码示例

\begin{lstlisting}
std::deque<int> mydeck(3, 100);        // 双端队列里初始化3个元素，都是100
std::list<int> mylist(2, 200);         // list 容器里初始化2个元素，都是200
std::queue<int> first;                 // 初始化一个空队列
std::queue<int> second(mydeck);        // 复制 mydeck 的内容初始化队列
std::queue<int, std::list<int> > third; // 初始化空队列，底层使用 list 容器
std::queue<int, std::list<int> > fourth(mylist);    // 复制 mylist 的内容初始化队列，底层使用 list 容器
\end{lstlisting}

\verb|std::deque|是不支持随机访问的，即不能使用\verb|operator[]|和\verb|at()|访问其中的某个元素。

\verb|empty()|判断队列是否为空

\verb|size()|获取队列中元素个数

\verb|front()|返回第一个元素的引用

\verb|back()|返回最后一个元素的引用

\verb|push()|和\verb|emplace()|入队，\verb|pop()|出队。入队元素个数会增加，出队后，元素个数会减少。

\paragraph{双向队列deque}

双端队列(double-ended queue)

\verb|std::deque|与\verb|std::queue|在入队出队上的区别，在于\verb|std::deque|可以从两个方向入队，也可以从两个方向上出队。所以他有4个增删元素的函数，\verb|push_back(elem)|，\verb|push_front(elem)|，\verb|pop_back()|，\verb|pop_front()|

\verb|std::deque|能够在开头插入元素，而且时间复杂度是常数的O(1)，而\verb|std::vector|在开头插入元素的时间复杂度是线性的O(N)

\verb|std::deque|支持随机访问，能使用\verb|operator[]|和\verb|at()|访问其中的某个元素。

既然支持随机访问，那么就支持插入操作\verb|insert()|和删除操作\verb|erase()|

\paragraph{优先级队列priority\_queue}

在优先级队列中，元素会按照优先级的顺序来排列，队首的元素是优先级最大的元素。每当有新元素要入队，都会进行判断，并在其合适的位置插入，而不是单纯的队首或者队尾。

\begin{lstlisting}
priority_queue<T, Container, Compare>
\end{lstlisting}

\begin{enumerate}
\item \verb|T|：存储数据元素的具体类型，如int、double等
\item \verb|Container|：指定优先级队列底层使用的基础容器(默认会使用\verb|std::vector|容器)，我们也可以指定使用\verb|std::deque|容器
\item \verb|Compare|：指定优先级队列中元素存储的排序规则，由该规则就可以确定每个元素的优先级高低。默认使用\verb|less<T>|，即从大到小的排序规则，此时构建的是最大堆。当然，我们也可以使用\verb|greater<T>|，即按照从小到大的排序规则来存储元素，此时构建的是最小堆
\end{enumerate}

\verb|top()|，返回第一个元素的引用。

\verb|push()|和\verb|pop()|，插入和弹出元素。

\verb|emplace()|直接在容器内构造元素，而\verb|push()|先创建临时副本，然后进行复制或者移动过程。

\subsubsection{链表 list, forward\_list(C++11)}

\verb|std::list|是双向链表。与\verb|std::vector|的区别在于，\verb|std::list|在任一位置增删元素，其时间复杂度都是常数的O(1)

\verb|std::list|是可反转容器，\verb|reverse()|可以反转链表。

\verb|std::list|不支持随机访问。

\verb|std::list|由于是双向链表，所以他的插入和弹出方法函数也有4个，\verb|push_back(elem)|，\verb|push_front(elem)|，\verb|pop_back()|，\verb|pop_front()|

\verb|std::forward_list|是单链表，是不可反转容器。

\verb|std::forward_list|只需要正向迭代器，而不需要双向迭代器，所以没有\verb|rbegin()|和\verb|rend()|

\subsubsection{栈 stack}

栈，先进后出FILO(first-in, last-out)

不允许随机访问栈元素，不允许遍历栈

\verb|std::stack|的常用操作见表\ref{std::stack的常用操作}

\begin{table}[htbp]\centering
\caption{std::stack的常用操作} \label{std::stack的常用操作}
\begin{tabular}{|l|p{20em}|}
\hline 函数 & 功能 \\ \hline
\verb|size()| & 返回stack的大小 \\ \hline
\verb|empty()| & 判断stack是否为空 \\ \hline
\verb|push(x)| & 将元素x压入栈 \\ \hline
(C++11)\verb|emplace()| & 将元素x压入栈 \\ \hline
\verb|top()| & 返回栈顶元素的引用 \\ \hline
\verb|pop()| & 将栈顶元素弹出 \\ \hline
\end{tabular}
\end{table}

\verb|push()|和\verb|emplace()|，将一个对象压入栈。区别在于\verb|push()|是先构造临时副本再复制或移动到栈顶，\verb|emplace()|直接在栈顶构造元素，效率更高。

\verb|push()|的接收参数有2种形式，\verb|const T&|和\verb|T&&|，前者是复制，后者是移动。

\subsection{关联容器}

\subsubsection{关联容器}

\textbf{关联容器(associative container)}是对容器概念的另一个改进。

关联容器将值(value)与键(key)关联在一起，通过键来查找值。

关联容器提供了对元素的快速访问。允许插入元素，但是不能指定元素插入的位置。

关联容器通常是使用某种树来实现的。

STL提供了4种关联容器

\begin{itemize}
    \item \verb|set|
    \item \verb|multiset|
    \item \verb|map|
    \item \verb|multimap|
\end{itemize}

\verb|set|，键具有唯一性，值的类型与键相同，而\verb|multiset|则允许有多个相同的键。

\verb|map|，键具有唯一性，值的类型与键不同，而\verb|multimap|则允许有多个相同的键。

\subsubsection{std::set}
\verb|std::set|底层是红黑树，所以查找的效率较高。

\verb|std::set|的基本操作见表\ref{std::set的常用操作}

\begin{table}[htbp]\centering
\caption{std::set的常用操作} \label{std::set的常用操作}
\begin{tabular}{|l|p{20em}|}
\hline 函数 & 功能 \\ \hline
\verb|size()| & 返回set的大小 \\ \hline
\verb|empty()| & 判断set是否为空 \\ \hline
\verb|set.insert(key_value)| & 插入元素。返回值是\verb|pair<set<int>::iterator,bool>|，bool标志着插入是否成功，而iterator代表插入的位置，若\verb|key_value|已经在set中，则iterator表示的\verb|key_value|在set中的位置 \\ \hline
\verb|inset(first,second)| & 将迭代器指向的范围，插入到当前set中 \\ \hline
\verb|set.erase(iterator it)| & 删除迭代器指向的元素 \\ \hline
\verb|set.erase(iterator first，iterator last)| & 删除迭代器指向的范围的所有元素 \\ \hline
\verb|size_type erase(const Key&key);| & 通过关键字，删除对应的元素 \\ \hline
\verb|find(key)| & 查找一个值为\verb|key|的元素，如果找得到，返回指向他的迭代器。如果没有找到，则返回超尾位置。 \\ \hline
\verb|lower_bound(key_value)| & 返回第一个大于等于\verb|key_value|的定位器 \\ \hline
\verb|upper_bound(key_value)| & 返回最后一个大于等于\verb|key_value|的定位器 \\ \hline
\end{tabular}
\end{table}

\verb|std::set|只提供了不可变迭代器，因为它不允许用户修改里面的值。只允许删去旧的，新增一个新的。

\subsubsection{std::map}
\verb|std::map|里面不会有重复的元素。

\verb|std::map|的底层是红黑树。

\verb|std::map|的基本操作见表\ref{std::map的常用操作}

由于\verb|std::map|的键与值的类型不同，所以其键值对是以\verb|std::pair<>|的形式存在的，从另外一个角度可以说，\verb|std::map|里面的所有元素都是\verb|std::pair<>|。

\begin{table}[htbp]\centering
	\caption{std::map的常用操作} \label{std::map的常用操作}
	\begin{tabular}{|l|p{20em}|}
		\hline 函数 & 功能 \\ \hline
		\verb|size()| & 返回map的大小 \\ \hline
		\verb|empty()| & 判断map是否为空 \\ \hline
		\verb|map[keyName] = valueName;| & 插入元素 \\ \hline
		\verb|map.insert(pair<tyep1, type2>(key, value));| & 插入元素，通过匿名的\verb|std::pair|容器\\
		\verb|dict.insert(make_pair("apple", "苹果"));| & 插入元素 \\ \hline
		\verb|map.insert({key, value});| & 插入元素，通过花括号进行列表初始化 \\ \hline
		\verb|emplace()| & 插入元素 \\ \hline
		\verb|[key]|或者\verb|at(key)| & 访问其中元素 \\
		\verb|at(key)| & 访问其中元素 \\ \hline
		\verb|map.erase(iterator it)| & 删除迭代器指向的元素 \\ \hline
		\verb|map.erase(iterator first，iterator last)| & 删除迭代器指向的范围的所有元素 \\ \hline
		\verb|size_type erase(const Key&key);| & 通过关键字，删除对应的元素 \\ \hline
		\verb|size_type count( const Key& key ) const;| & 查找指定的元素在容器中出现的次数 \\ \hline
		\verb|find(key)| & 查找一个值为\verb|key|的元素，如果找得到，返回指向他的迭代器。如果没有找到，则返回超尾位置。 \\ \hline
		(C++20)\verb|bool contains(key)| & 返回容器中是否有值为\verb|key|的元素 \\ \hline
	\end{tabular}
\end{table}

\paragraph{方括号[]与at()的区别}
\verb|std::map|读取其中元素，使用\verb|operator[]|，比如\verb|dict["banana"];|，也可以使用\verb|at()|，区别在于\verb|at()|如果访问了不存在的元素会抛出异常。

而\verb|operator[]|访问到了不存在的元素，会为你创建一个这个元素的键值对。有的时候，这会造成误解。程序反悔了一个值，但是这个值是刚创建的，而并不是之前就存储进去的，真相掩盖在了一个默认值之下。

\paragraph{反向查找表}
对于较大的线性表，查找的复杂度为$O(N)$，如果查找N次，则查找复杂度上升为$O(N^2)$。

可以利用\verb|std::map|的特性，构建反向查找表，构建线性表中元素与index之间的映射，快速通过元素找到index
\begin{lstlisting}
std::vector<std::string> arr = {...};  // 假设这里是一个很大的数组
std::map<std::string, size_t> arrinv;
for (size_t = 0; i < arr.size(); i++) {
    arrinv[arr[i]] = i;  // 构建反向的映射关系，通过元素查找index
}
\end{lstlisting}

这样，查找元素的时候可以通过\verb|arrinv.at("tom")|这样的形式，返回其在\verb|std::vector|中的index。这样，进行N次查找的复杂度可以降低至$O(Nlog(N))$

\paragraph{value\_type}
STL容器中的元素，都可以通过\verb|value_type|来查询其类型，这个技巧常用于泛型编程中。
\begin{lstlisting}
std::set<int>::value_type
std::vector<int>::value_type
\end{lstlisting}

\paragraph{加typename修饰}
当容器至少有1个不确定的类型T作为模板参数时，需要在前面加上\verb|typename|作为修饰。因为此时编译器对于\verb|<, *|这两个符号产生了二义性。加上\verb|typename|修饰，就明确了后面的是一串类型表达式，所以\verb|<|是模板的一部分，而不是小于号，\verb|*|是解引用运算符，而不是乘法运算符。

含有\verb|T|的表达式称为dependant-type。

\subsection{(C++11)无序关联容器}
无序关联容器，主要包含以下几种
\begin{itemize}
	\item \verb|std::unordered_set|
	\item \verb|std::unordered_multiset|
	\item \verb|std::unordered_map|
	\item \verb|std::unordered_multimap|
\end{itemize}

\paragraph{无序关联容器与有序关联容器的一般性区别}
\begin{itemize}
    \item 底层存储的数据结构不同。带有\verb|unordered|前缀的无序关联容器，与对应的关联容器相比，使用的底层数据结构是不同的。无序关联容器使用哈希表来存储，而关联容器使用红黑树来存储。
    \item 需要使用的比较函数不同。无序关联容器，由于使用哈希表存储，所以需要定义\verb|hash_value()|函数并且重载\verb|operator==|。而关联容器，需要定义\verb|operator<|，来比较元素之间的关系。
    \item 排序。无序关联容器，如名称所示，他是不带有顺序的。而普通的关联容器是按顺序排列所有元素的，增删元素时也会对应改动原顺序。由于其有序性，还支持\verb|lower_bound| 和 \verb|upper_bound|这样的范围查询。
    \item 复杂度。无序关联容器平均 $O(1)$ 的插入、删除和查找操作，这是因为它们基于哈希表实现。如果发生大量的哈希冲突，可能退化至$O(n)$。而普通的关联容器提供了 $O(log n)$ 的插入、删除和查找操作，因为它们是基于红黑树实现的，而且他的复杂度并不会随着情况变化而发生恶化，会一直维持在$O(log n)$。
    \item 内存开销。无序关联容器的内存开销稍大一些。
\end{itemize}

\subsubsection{std::unordered\_map}
\paragraph{遍历}
使用与想要遍历的\verb|std::unordered_map|容器相同类型的迭代器，比如下面的\verb|unordered_map<int, int>::iterator|进行遍历。从\verb|begin()|遍历到超尾位置\verb|end()|
\begin{lstlisting}
unordered_map<int, int> hmap{ {1,10},{2,12},{3,13} };
unordered_map<int, int>::iterator iter = hmap.begin();
for( ; iter != hmap.end(); iter++) {
    cout << "key: " <<  iter->first  << "value: " <<  iter->second <<endl;
}     
\end{lstlisting}

\subsection{其他容器}
\subsubsection{std::pair}
\verb|std::pair|将两个数据合成一个数据。比传统的结构体更高级，更接近现代C++的用法。

头文件\verb|<utility>|

\verb|make_pair(v1, v2);|以v1和v2的值创建一个\verb|std::pair|对象。

\verb|.first|和\verb|.second|返回\verb|std::pair|中的第一个元素和第二个元素。注意这里是没有括号的。

\subsubsection{std::tuple}
\verb|std::tuple|可以将多个不同类型的值打包成一个

举例\verb|auto tup = std::tuple<int, float, char>(3, 3.14f, ‘h’);|

\paragraph{打包元组std::make\_tuple}
\verb|auto result2 = std::make_tuple(7, 9.8, "text");|

由上面的写法可以看出，使用\verb|make_tuple|可以无需显式地支出容器中每个成员的类型。

\verb|std::tuple|可以用于有多个返回值的函数，直接\verb|return {a, b, c};|即可

\paragraph{获取元素std::get}
获取其中元素使用\verb|std::get|

\verb|int first_member = std::get<0>(tup);|

\verb|float second_member = std::get<1>(tup);|

\paragraph{(C++17)利用结构化绑定解包tuple}
利用"结构化绑定"语法取解包一个\verb|std::tuple|

\verb|auto[first_m, second_m, third_m] = tup;|

"结构化绑定"可以用于任何自定义的类。

\paragraph{使用std::tie解包tuple}
使用\verb|std::tie|也可以解包tuple
\begin{lstlisting}
std::tuple<int,float,char> mytuple;
mytuple = std::make_tuple (10, 2.6, 'a');
std::tie(myint, std::ignore, mychar) = mytuple;
\end{lstlisting}

\paragraph{std::ignore可以作为std::tie的占位符}
任何值均可赋给而无效果的未指定类型的对象。目的是令 \verb|std::tie| 在解包 \verb|std::tuple| 时作为不使用的参数的占位符使用。
\begin{lstlisting}
std::tie(std::ignore, inserted)
\end{lstlisting}

\paragraph{std::tuple\_size}
可以获取\verb|std::tuple|中元素的个数

是一个类模板

参考代码示例\ref{tuple_size}

程序输出\verb|mytuple has 3 elements|

\paragraph{std::tuple\_cat}
可以拼接\verb|std::tuple|

可以看代码示例\ref{tuple_cat}

其程序输出为\verb|(10, Test, 3.14, Foo, bar, 10, Test, 3.14, 10)|

\begin{code_example}\label{tuple_size}
\begin{lstlisting}
// tuple_size
#include <iostream>     // std::cout
#include <tuple>        // std::tuple, std::tuple_size

int main ()
{
    std::tuple<int,char,double> mytuple (10,'a',3.14);
    
    std::cout << "mytuple has ";
    std::cout << std::tuple_size<decltype(mytuple)>::value;
    std::cout << " elements." << '\n';
    
    return 0;
}
\end{lstlisting}
\end{code_example}

\begin{code_example}\label{tuple_cat}
\begin{lstlisting}
#include <iostream>
#include <tuple>
#include <string>

// 打印任何大小 tuple 的帮助函数
template<class Tuple, std::size_t N>
struct TuplePrinter {
    static void print(const Tuple& t) 
    {
        TuplePrinter<Tuple, N-1>::print(t);
        std::cout << ", " << std::get<N-1>(t);
    }
};

template<class Tuple>
struct TuplePrinter<Tuple, 1> {
    static void print(const Tuple& t) 
    {
        std::cout << std::get<0>(t);
    }
};

template<class... Args>
void print(const std::tuple<Args...>& t) 
{
    std::cout << "(";
    TuplePrinter<decltype(t), sizeof...(Args)>::print(t);
    std::cout << ")\n";
}
// 结束帮助函数

int main()
{
    std::tuple<int, std::string, float> t1(10, "Test", 3.14);
    int n = 7;
    auto t2 = std::tuple_cat(t1, std::make_tuple("Foo", "bar"), t1, std::tie(n));
    n = 10;
    print(t2);
}
\end{lstlisting}
\end{code_example}

\subsubsection{std::bitset}
头文件\verb|<bitset>|

功能：表示一系列比特，方便进行位运算。

\paragraph{构造函数}
可以使用由1和0组成的字符串进行初始化，包含其他字符会报错。
\begin{lstlisting}
    std::bitset<8> bst("10101");
\end{lstlisting}

使用十进制数，十六进制数进行初始化，会转换为对应的二进制数值存入。
\begin{lstlisting}
    std::bitset<8> bst2(121u);
\end{lstlisting}

满足可拷贝构造 (CopyConstructible) 及可拷贝赋值 (CopyAssignable) 的要求

\paragraph{常用的函数}
\begin{table}[htbp]\centering
    \caption{std::bitset的常用函数} \label{std::bitset的常用函数}
    \begin{tabular}{|l|p{20em}|}
        \hline 常用函数 & 功能 \\ \hline
        \verb|bool test(std::size_t pos) const;| & 将指向的元素以\verb|bool|的形式返回，如果超限会返回\verb|std::out_of_range| \\ \hline
        \verb|bool all()| & 如果全是1则返回true，否则返回false \\ \hline
        \verb|bool any()| & 存在1位是1则返回true \\ \hline
        \verb|bool none()| & 全都不是1则返回true \\ \hline
        \verb|bitset& set()| & 设置所有位为1 \\ \hline
        \verb|bitset& set(std::size_t pos, bool value = true);| & 设置指定位置为指定的值，只能传入\verb|bool|类型 \\ \hline
        \verb|count()| & 返回其中为1的位的个数 \\ \hline
        \verb|flip()| & 反转该位，1变成0，0变成1 \\ \hline
        \verb|reset()| & 清除所有的位 \\ \hline
        \verb|to_string()| & 转换为字符串 \\ \hline
        \verb|to_ulong()| & 转换为unsigned long整型 \\ \hline
    \end{tabular}
\end{table}

\paragraph{运算符}
\verb|std::bitset|支持一些运算符，快速方便地进行位运算。
\begin{lstlisting}
std::bitset<4> b1("1100");
std::bitset<4> b2("1010");
std::bitset<4> b3 = b1 & b2; // bitwise AND
std::bitset<4> b4 = b1 | b2; // bitwise OR
std::bitset<4> b5 = b1 ^ b2; // bitwise XOR
std::bitset<4> b6 = ~b1;     // bitwise NOT

std::bitset<4> b1("1100");
std::bitset<4> b2 = b1 << 1; // left shift
std::bitset<4> b3 = b1 >> 1; // right shift
\end{lstlisting}

\begin{table}[htbp]\centering
    \caption{std::bitset支持的运算符} \label{std::bitset支持的运算符}
    \begin{tabular}{|l|p{20em}|}
        \hline 运算符 & 功能 \\ \hline
        \verb|<<| & 左移一位\\ \hline
        \verb|>>| & 右移一位\\ \hline
        \verb|&| & 按位与\\ \hline
        | & 按位或\\ \hline
        \verb|^| & 按位异或\\ \hline
        \verb|~| & 取反\\ \hline
    \end{tabular}
\end{table}
    
\subsubsection{(C++17)std::optional}\label{(C++17)std::optional}
如果函数需要返回多个值的同时，还要返回一个\verb|bool|型表示是否成功，则可以使用\verb|std::optional<T>|容器，成功时返回\verb|T|，失败时则返回\verb|std::nullopt|

\paragraph{示例}
下面是一个简单的案例，是如何使用\verb|std::optional|的。\verb|std::optional<std::string>|通过这样的声明，可以让该函数的返回实现这样的一种功能，就是他可以返回\verb|std::string|，也可以返回\verb|std::nullopt|。可以看到函数体中\verb|return|语句也确实返回了两种类型。
\begin{lstlisting}
std::optional<std::string> tItem::findShortName()
{
	if (hasShortName)
	{
		return mShortName;
	}
	return std::nullopt;
}

// 使用
std::optional<std::string> shortName = item->findShortName();
if (shortName)
{
	PRITNT(*shortName);
}
\end{lstlisting}

\paragraph{构造}
\verb|std::optional|的构造方式，支持以下几种。
\begin{lstlisting}
//初始化为空
std::optional<int> emptyInt;
std::optional<double> emptyDouble = std::nullopt;
//直接用有效值初始化
std::optional<int> intOpt{10};
std::optional intOptDeduced{10.0}; // auto deduced
//使用make_optional
auto doubleOpt = std::make_optional(10.0);
auto complexOpt = std::make_optional<std::complex<double>>(3.0, 4.0);
//使用in_place
std::optional<std::complex<double>> complexOpt{std::in_place, 3.0, 4.0};
std::optional<std::vector<itn>> vectorOpt{std::in_place, {1, 2, 3}};
//使用其它optional对象构造
auto optCopied = vectorOpt;
\end{lstlisting}

\paragraph{为什么std::optional的构造函数使用std::in\_place}
是用来区分到底是用optional<T>的默认构造函数还是T的默认构造函数，参见\href{https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2013/n3527.html#rationale.emplace}{N3527}

为了把 \verb|std::optional| 的构造函数里的参数，完美转发给T的构造函数(以构造 \verb|T| )，就需要一个标识符。最开始这个标识符被叫做 \verb|emplace| ，后来被更改为 \verb|std::in_place|。

\begin{lstlisting}
optional<Big> ob{emplace, "1"}; // 在原地调用Big{"1"}，不发生移动 
								// calls Big{"1"} in place (no moving)

optional<Big> oc{emplace};      // 在原地调用Big{}，不发生移动 
								// calls Big{} in place (no moving)

optional<Big> od{};             // 没有存储任何值
								// creates a disengaged optional
\end{lstlisting}

\paragraph{什么情况下使用std::in\_place进行构造}
比如对象不支持移动和拷贝

\paragraph{其他操作}
\verb|operator*| 返回内部存储对象的引用，\verb|operator->| 返回指向内部存储对象的指针。

\verb|value()|返回内部存储对象的值，当\verb|optional|为空时抛出\verb|std::bad_optional_access|异常。

\verb|value_or(defaultValue)|，\verb|optional|有有效值时返回有效值，否则返回默认值
\begin{lstlisting}
std::optional<int> optInt(100);
std::cout << "value is " << optInt.value_or(10) << std::endl;
\end{lstlisting}

\paragraph{修改存储的值}
对于\verb|std::optional<tStudent> optStudent;|，使用\verb|optStudent.emplace("Bob")|，可以构造名字为“Bob”的tStudent对象存储在optional对象中。相当于\verb|optStudent = tStudent{"Bob"};|。

\verb|optStudent.emplace("Steve")|，"Bob"对象析构，构造"Steve"。

\verb|optStudent.reset();|，"Steve"对象析构。

\paragraph{比较大小}
\verb|std::optional|可以直接使用比较运算符，比较的是其存储的有效值的大小。当然，前提是有效值必须是可以比较大小的类型，也就是他们必须支持比较运算符。

\verb|std::nullopt|比较特殊，他小于任何其他的有效值。

\subsubsection{(C++17)std::variant}\label{(C++17)std::variant}
提供了功能更强大、更安全的\verb|union|

要么存储类型A，要么存储类型B

\verb|std::variant<int, float> v;|

如果要取出其中某个类型的值，可以用\verb|std::get<int>(v);|

如果要取得类型不存在，则会抛出异常\verb|std::bad_variant_access|

判断当前存储的类型是不是某一种类型，\verb|std::holds_alternative<int>(v)|，返回\verb|true|或者\verb|false|

返回当前有效的类型的index值，\verb|v.index()|

\subsubsection{(C++17)std::visit()}
可以使用\verb|std::visit()|来遍历访问\verb|std::variant|里的所有对象

\verb|std::visit|的作用是，将容器中的对象传递给可调用对象中的一个，具体调用哪个可调用对象由该对象所包含的对象的实际类型决定。

可以看下面的代码示例

\begin{lstlisting}
#include <iostream>
#include <variant>
​
int main() {
    std::variant<int, float, std::string> v;
    ​
    v = 10;
    std::visit([](auto&& arg) { std::cout << arg << '\n'; }, v);
    ​
    v = 3.14f;
    std::visit([](auto&& arg) { std::cout << arg << '\n'; }, v);
    ​
    v = "fanxing";
    std::visit([](auto&& arg) { std::cout << arg << '\n'; }, v);
    ​
    return 0;
}
\end{lstlisting}

程序输出如下

\begin{lstlisting}
10
3.14
hello
\end{lstlisting}

另一段代码示例

\begin{lstlisting}
#include <iostream>
#include <variant>
#include <type_traits>
​
int main()
{
    std::variant<int, float, std::string> v{10086};
    std::visit(
    [](auto&& arg)
    {
        
        using T = std::decay_t<decltype(arg)>;
        if constexpr (std::is_same_v<T, int>)
        {
            std::cout << "arg is an integer with value: " << arg << '\n';
        }
        ​
        else if constexpr (std::is_same_v<T, float>)
        {
            std::cout << "arg is a float with value: " << arg << '\n';
        }
        ​
        else if constexpr (std::is_same_v<T, std::string>)
        {
            std::cout << "arg is a string with value: " << arg << '\n';
        }
    },
    v);
    ​
    return 0;
}
\end{lstlisting}

\verb|std::visit|的函数原型

\begin{lstlisting}
// since C++17
template <class Visitor, class... Variants>
constexpr visit( Visitor&& vis, Variants&&... vars );
// since C++20
template <class R, class Visitor, class... Variants>
constexpr R visit( Visitor&& vis, Variants&&... vars );
\end{lstlisting}

std::visit和std::variant这类，被称为静态多态。优点是开销小，存储空间固定。缺点是类型固定，不能运行时扩充。与虚函数，抽象类所构成的动态多态相对应


\section{函数对象}
C++ Primer Plus (6th edition)原书第707页，第16.5节

函数对象，也叫函数符，或者仿函数(functor)(本笔记中，统一使用函数对象这个术语来表示functor)。

函数对象是可以以函数方式与圆括号运算符\verb|()|结合使用的对象，包括函数名、指向函数的指针、重载了\verb|()|运算符的类对象(即，定义了\verb|operator()()|的类或者结构体)

\subsubsection{思考与for\_each()有关的问题}
\begin{lstlisting}
for_each(books.begin(), books.end(), ShowReview);
\end{lstlisting}

第三个参数，可以是常规函数，也可以是函数对象。那么这就引出了一个问题，\verb|for_each()|函数的第三个参数，应该如何声明？

首先，它不能声明为函数指针，因为指针必须包含一个其指向的数据类型。而作为模板，它是不能预先指定一个类型的。

STL通过模板解决了这个问题。
\begin{lstlisting}
template<class InputIterator, class Funciton>
Function for_each(InputIterator first, InputIterator last, Function f);
...

// for_each()中第3个参数，传入了一个函数对象。
for_each(books.begin(), books.end(), ShowReview);

// 有这样的函数声明
void ShowReview(const Review &);
\end{lstlisting}

这样，标识符\verb|ShowReview|的类型将成为\verb|void(*)(const Review &)|，这也是赋给模板参数\verb|Function|的类型。对于不同的函数调用，模板参数\verb|class Function|可以表示具有重载\verb|()|运算符的类的类型，指的就是函数对象。

\subsection{函数对象概念}\label{函数对象概念}
\begin{itemize}
\item 生成器(generator)是不用参数就可以调用的函数对象
\item 一元函数(unary function)是用一个参数可以调用的函数对象
\item 二元函数(binary function)是用两个参数可以调用的函数对象
\item 谓词(predicate)是返回bool值的一元函数
\item 二元谓词(binary predicate)是返回bool值的二元函数
\end{itemize}

有一些STL函数需要谓词参数或者二元谓词参数。例如\verb|std::sort()|

\begin{lstlisting}
bool WorseThan(const Review & r1, const Review & r2);
...
sort(books.begin(), books.end(), WorseThan);
\end{lstlisting}

list模板有一个成员函数\verb|std::remove_if()|，接收一个谓词作为参数。

\begin{lstlisting}
bool tooBig(int n){ return n > 100; }
list<int> scores;
...
scores.remove_if(tooBig);
\end{lstlisting}

\subsection{预定义的函数对象}

C++ Primer Plus (6th edition)原书第710页，第16.5.2节

STL定义了多个基本函数对象，执行相加、比较等基础的操作。

\subsubsection{通过transform()引出预定义的函数对象}

看下面的代码示例，\verb|transform()|函数，前两个参数接收指定容器区间的迭代器，第三个参数是将结果复制到何处的迭代器，最后一个参数是一个函数对象，它被应用于区间中的每一个元素。代码示例中，计算容器中每个元素的迭代器，将结果发送到输出流。如果将\verb|out|替换为\verb|gr8.begin()|后，新的值将覆盖旧的值。

这里所使用的函数对象，是接收单个参数的函数对象(即，一元函数)。

\begin{lstlisting}
const int LIM = 5;
double arr1[LIM] = {36, 39, 42, 45, 48};
vector<double> gr8(arr1, arr1 + LIM);
ostream_iterator<double, char> out(cout, " ");
transform(gr8.begin(), gr8.end(), out, sqrt);
\end{lstlisting}

\verb|transform()|函数的另外一个版本。第三个参数标识另一个区间的起始位置，例如\verb|m8|是一个\verb|vector<double>|对象，\verb|mean(double, double)|返回两个值的平均值

\begin{lstlisting}
transform(gr8.begin(), gr8.end(), m8.begin(), out, mean);
\end{lstlisting}

\subsubsection{如何定义一个负责相加的函数对象}

头文件\verb|functional|里定义了多个模板类函数对象，其中就包括\verb|plus<>()|

下面的代码示例中，传递给\verb|transform()|的似乎构造出来的函数对象，而且是一个匿名的。

更多的预定义的函数对象，可以见表\ref{运算符和相对应的函数对象}

\begin{lstlisting}
#incldue <functional>
...
plus<double> add;
double y = add(2.2, 3.4);
...
transform(gr8.begin(), gr8.end(), m8.begin(), out, plus<double>());
\end{lstlisting}

\begin{table}[htbp]\centering
\caption{运算符和相对应的函数对象} \label{运算符和相对应的函数对象}
\begin{tabular}{cl}
\hline 运算符 & 相对应的函数对象 \\ \hline
\verb|+| & \verb|plus| \\
\verb|-| & \verb|minus|或者\verb|negate| \\
\verb|*| & \verb|multiplies| \\
\verb|/| & \verb|divides| \\
\verb|%| & \verb|modulus| \\
\verb|==| & \verb|equal_to| \\
\verb|!=| & \verb|not_equal_to| \\
\verb|>| & \verb|greater| \\
\verb|<| & \verb|less| \\
\verb|>=| & \verb|greater_equal| \\
\verb|<=| & \verb|less_equal| \\
\verb|&&| & \verb|logical_and| \\
\verb+||+ & \verb|logical_or| \\
\verb|!| & \verb|logical_not| \\
\hline
\end{tabular}
\end{table}

\subsection{自适应函数对象和函数适配器}

C++ Primer Plus (6th edition)原书第711页，第16.5.3节

表\ref{运算符和相对应的函数对象}列出的预定义函数对象都是自适应的。

实际上STL有5个相关的概念。

\begin{itemize}
\item 自适应生成器(adaptable generator)
\item 自适应一元函数(adaptable unary)
\item 自适应二元函数(adaptable binary function)
\item 自适应谓词(adaptable predicate)
\item 自适应二元谓词(adaptable binary predicate)
\end{itemize}

\subsubsection{为什么会产生"自适应"这一概念}

因为函数对象需要提前知道参数类型和返回类型的\verb|typedef|，分别用\verb|result_type|、\verb|first_argument_type|、\verb|second_argument_type|来存放这些类型，其作用可以由其命名得知。

例如，\verb|plus<int>|对象的返回类型被标识为\verb|plus<int>::result_type|，这是\verb|int|的\verb|typedef|

STL提供了\textbf{函数适配器类}

\subsubsection{以transform()为例说明如何使用函数适配器}
以下面的代码为例。
\begin{lstlisting}
transform(gr8.begin(), gr8.end(), out, sqrt);    
\end{lstlisting}

假如现在有一个需求，想要将\verb|gr8|里面每个元素都增加2.5倍，则需要使用一个接受一元函数的\verb|transform()|，而且还要有一个函数对象来负责执行"增加2.5倍"这个过程。

预定义的函数对象\verb|multiplies()|提供了基础的乘法运算，但是它是一个二元函数。因此这里就需要用一个\textbf{函数适配器}，将二元函数对象转换为一元函数对象。

\subsubsection{binder1st和binder2nd}

StL提供了\verb|binder1st|和\verb|binder2nd|类来完成这一过程

假设有一个自适应二元函数对象\verb|f2()|，则可以用如下的方式。在使用单个参数调用\verb|f1(x)|时，返回的值与\verb|f2(x, val)|是相同的，
\begin{lstlisting}
binder1st(f2, val) f1;
\end{lstlisting}

STL又提供了\verb|bind1st|来简化\verb|binder1st|的使用过程
\begin{lstlisting}
bind1st(multiplies<double>(), 2.5)
\end{lstlisting}

因此，用在\verb|transform()|中的效果就是这样的
\begin{lstlisting}
transform(gr8.begin(), gr8.end(), out, bind1st(multiplies<double>(), 2.5));
\end{lstlisting}

\verb|binder2nd|和\verb|bind2nd|类与之相似，只是将常数赋值给第二个参数。

\subsubsection{std::bind1st和std::bind2nd}
在代码示例\ref{std::bind1st和std::bind2nd代码示例}中，展示了\verb|std::bind1st|和\verb|std::bind2nd|的使用方式，他们分别能够将二元函数对象转变为一元函数对象。二元函数对象在调用的时候，原来是需要2个成员的，而其中一个由一元函数对象提供，另一个则在一元函数对象的声明中给定了，由\verb|std::bind1st|和\verb|std::bind2nd|给出。

对于\verb|bind1st(plus<int>(), 10)|来说，本来\verb|plus<int>()|是一个二元函数对象，现在让他的第一个参数固定为10，所以\verb|b1st_plus(b)|就相当于\verb|10+b|，而\verb|b2nd_plus|就相当于\verb|b+10|，因为它把10绑定到了第二个参数上。

\begin{code_example}\label{std::bind1st和std::bind2nd代码示例}
\begin{lstlisting}
#include "stdafx.h"
#include <iostream>
#include <functional>
#include <vector>
#include <algorithm>
#pragma warning(disable:4996)

using namespace std;

int main(int argc, char *argv[])
{
    int a = 10;
    int b = 20;
    
    binder1st<plus<int>> b1st_plus = bind1st(plus<int>(), 10);
    binder1st<minus<int>> b1st_minus = bind1st(minus<int>(), 10);
    
    cout << "10+b=" << b1st_plus(b) << endl;
    cout << "10-5=" << b1st_minus(5) << endl;
    
    binder2nd<plus<int>> b2nd_plus = bind2nd(plus<int>(), 10);
    binder2nd<minus<int>> b2nd_minus = bind2nd(minus<int>(), 10);
    
    cout << "b+10=" << b2nd_plus(b) << endl;
    cout << "5-10=" << b2nd_minus(5) << endl;
    
    vector<int> v1;
    for (int i = 0; i < 10; i++)
    {
        v1.push_back(i);
    }
    //使用count_if获得容器v1中大于或等于4的个数
    int count = count_if(v1.begin(),v1.end(),bind1st(less_equal<int>(),4));
    cout << "bind1st v1 >= 4 count=" << count << endl;
    
    count = count_if(v1.begin(), v1.end(), bind2nd(greater_equal<int>(), 4));
    cout << "bind2nd v1 >= 4 count=" << count << endl;
    return 0;
    
}
\end{lstlisting}
\end{code_example}

程序输出
\begin{lstlisting}
10+b=30
10-5=5
b+10=30
5-10=-5
\end{lstlisting}

\subsubsection{自定义函数适配器}
\begin{code_example}\label{}
\begin{lstlisting}
#include "stdafx.h"
#include <iostream>
#include <functional>
#include <vector>
#include <algorithm>
#pragma warning(disable:4996)
using namespace std;

template<typename oper_fun, typename param>
class test_binder1st{
    public:
    test_binder1st(oper_fun operation, param first_param)
    {
        oper = operation;
        first = first_param;
    }
    param operator ()(param second)
    {
        return oper(first, second);
    }
    private:
    oper_fun oper;
    param first;
};

template<typename oper_fun, typename param>
test_binder1st<oper_fun, param> test_bind1st(oper_fun operation, param first)
{
    return test_binder1st<oper_fun, param>(operation, first);//调用的是上面test_binder1st的构造函数
}

int main(int argc, char *argv[])
{
    int a = 10;
    int b = 20;
    
    test_binder1st<plus<int>, int> b1st_plus = test_bind1st(plus<int>(), 10);
    test_binder1st<minus<int>, int> b1st_minus = test_bind1st(minus<int>(), 10);
    
    cout << "10+b=" << b1st_plus(b) << endl;
    cout << "10-5=" << b1st_minus(5) << endl;
    
    
    
    vector<int> v1;
    for (int i = 0; i < 10; i++)
    {
        v1.push_back(i);
    }
    //使用count_if获得容器v1中大于或等于4的个数
    int count = count_if(v1.begin(),v1.end(),test_bind1st(less_equal<int>(),4));
    cout << "bind1st v1 >= 4 count=" << count << endl;
    
    return 0;
    
}

// 程序输出
// 10+b=30
// 10-5=5
// bind1st v1 >= 4 count=6    
\end{lstlisting}
\end{code_example}

\subsection{(C++11)std::bind}\label{(C++11)std::bind}
\verb|std::bind|机制并不是新标准中首次提出，而是对旧版本中\verb|bind1st|和\verb|bind2st|的合并

\verb|std::bind|的头文件是 \verb|<functional>|，它是一个函数适配器，接受一个可调用对象(callable object)，生成一个新的可调用对象来适应原对象的参数列表。举例来说，可以把一个函数更改成另一个参数列表的函数，比如说原来需要3个参数，现在变成是1个参数，另外2个参数在使用\verb|std::bind|时配置了。

\subsubsection{理解占位符}
我们将被\verb|bind|的函数称为\verb|func|，将\verb|bind|之后的函数称为\verb|funcBind|

易知，绑定前后两个函数的参数列表肯定是不同的。

假如\verb|func|需要2个参数，\verb|funcBind|需要1个参数。那么\verb|func|所需要的另外一个参数，应该如何传递呢？(或者说，如何制定这个参数呢)
\begin{lstlisting}
auto funcBind = std::bind(func, std::placeholder::_1, 100);
// funcBind(5);
// func(5, 100);
\end{lstlisting}

通过上面的代码示例可以看出，在生成新的可调用对象的时候(或者说，执行绑定的过程中)，多余的那个参数已经被指定了，代码中\verb|100|就是被提前指定的那个参数。所以下面\verb|funcBind(5);|就等价于\verb|func(5, 100);|

而\verb|func|参数列表中的第一个参数，正是通过占位符，来预留了位置。在以后调用新的可调用对象时，才会知道这个参数的值，把他刚好放在占位符的位置。

\verb|std::placeholder::_1|接收的是\verb|funcBind|参数列表中的第一个参数，同理\verb|std::placeholder::_2|接收的是第二个参数。

\subsubsection{绑定普通函数}
在下面的代码示例中，\verb|NewCallable(10)|，实际上就变成了\verb|callableFunc(10, 2)|

参数列表中，第一个参数，我们不预先给定，所以要用占位符\verb|std::placeholders|来把他占位。而第二个参数，才需要预先设定为2。

原来的函数\verb|double(double, double)|被变成了\verb|double(double)|，而且特别地，是把\verb|y|取代了，而不是参数列表中的第一个参数\verb|x|。
\begin{lstlisting}
double callableFunc (double x, double y) {return x/y;}
auto NewCallable = std::bind (callableFunc, std::placeholders::_1, 2);  
std::cout << NewCallable (10) << '\n';                       
\end{lstlisting}

\subsubsection{绑定成员函数}
绑定成员函数时，第一个参数表示对象的成员函数的指针，第二个参数表示对象的地址。
\begin{lstlisting}
class Base
{
    public:
    void display_sum(int a1, int a2)
    {
        std::cout << a1 + a2 << '\n';
    }
    
    int m_data = 30;
};
int main() 
{
    Base base;
    auto newiFunc = std::bind(&Base::display_sum, &base, 100, std::placeholders::_1);
    f(20); // should out put 120. 
}
\end{lstlisting}

\subsubsection{按值传递变成按引用传递}
默认情况下，\verb|bind|是把非占用符的参数，按值传递给\verb|bind|内部的可调用对象的。但是有些情况下，我们不希望发生按值传递，或者有些类的对象他是禁止拷贝的，所以不支持按值传递，这个时候我们希望使用按引用传递，那么就需要使用\verb|std::ref|
\begin{lstlisting}
#include <iostream>
#include <functional>
#include <vector>
#include <algorithm>
#include <sstream>
using namespace std::placeholders;
using namespace std;

ostream & printInfo(ostream &os, const string& s, char c)
{
    os << s << c;
    return os;
}

int main()
{
    vector<string> words{"welcome", "to", "C++11"};
    ostringstream os;
    char c = ' ';
    for_each(words.begin(), words.end(), [&os, c](const string & s){os << s << c;} );
    cout << os.str() << endl;
    
    ostringstream os1;
    // ostream不能拷贝，若希望传递给bind一个对象，
    // 而不拷贝它，就必须使用标准库提供的ref函数
    for_each(words.begin(), words.end(), bind(printInfo, ref(os1), _1, c));
    cout << os1.str() << endl;
}
\end{lstlisting}

\subsection{std::function}\label{std::function}
创建一个名为\verb|fdci|的\verb|std::function|对象，其调用特征标表明，它接收1个char类型1个int类型，返回1个double类型。

\begin{lstlisting}
    std::function<double(char, int)> fdci;
\end{lstlisting}

可以将任何等于这个调用特征标的可调用对象赋值给fdci对象。

另外两种使用function的方式

\begin{enumerate}
    \item 使用临时的function对象
    \item 在模板中将参数声明为funciton包装器对象
\end{enumerate}

\paragraph{使用临时的function对象}
\begin{lstlisting}
typedef function<double(double)> fdd;
use_f(y, fdd(dub));
\end{lstlisting}

\paragraph{在模板中将参数声明为funciton包装器对象}
直接在模板定义中，将第二个参数声明为function包装器对象

以前的模板是
\begin{lstlisting}
template <typename T, typename F>
T use_f(T v, F f)
{}
\end{lstlisting}

更改之后的模板是
\begin{lstlisting}
template <typename T>
T use_f(T v, std::function<T(T)> f)
{}
\end{lstlisting}

\paragraph{社区提问 - std::function什么时候申请堆内存}
\href{https://www.zhihu.com/question/1355501449}{原贴地址https://www.zhihu.com/question/1355501449}

对于如下定义的两个\verb|struct| \verb|A|和\verb|B|来说
\begin{lstlisting}
struct A {
    int value;
    A(int v) : value(v) {}
    A(const A &other) : value(other.value) {}
};
struct B {
    int value;
    B(int v) : value(v) {}
};
\end{lstlisting}

在主函数中有如下的语句。分别以花括号的方式初始化了\verb|a|和\verb|b|这两个对象，并创建了\verb|std::function|对象，使用lambda表达式来进行捕获当前作用域内的\verb|a|和\verb|b|对象。
\begin{lstlisting}
A a{0};
B b{0};
printf("----------------------------------1\n");
std::function<void()> funcA = [a] {};
printf("----------------------------------2\n");
std::function<void()> funcB = [b] {};
printf("----------------------------------3\n");
\end{lstlisting}

\paragraph{运行表现}
代码的运行表现是，捕获\verb|a|的时候发生了堆内存申请，而捕获\verb|b|的时候没有。

我们可以手动重载\verb|new, new[], delete, delete[]|运算符，来观测堆内存的申请现象。
\begin{lstlisting}
void *operator new(std::size_t size) {
    printf("malloc %zu\n", size);
    return malloc(size);
}
void *operator new[](std::size_t size) {
    printf("malloc[] %zu\n", size);
    return malloc(size);
}
void operator delete(void *phead) {
    printf("free\n");
    free(phead);
}
void operator delete[](void *phead) {
    printf("free[]\n");
    free(phead);
}
\end{lstlisting}

终端的输出为
\begin{lstlisting}
----------------------------------1
malloc 4
----------------------------------2
----------------------------------3
free
\end{lstlisting}

\paragraph{分析原因}
\verb|struct A|和\verb|struct B|的主要区别在于，\verb|A|有自定义的拷贝构造函数。C++称之为non-trivial copy constructor，非平凡拷贝构造函数。lambda表达式需要申请堆内存来存储对象\verb|a|的副本。

有关于trivial和non-trivial(平凡类型、非平凡类型)的概念，请参见第\ref{平凡类型}节。

而\verb|B|没有自定义的拷贝构造函数(或者说，没有显式地给出一个拷贝构造函数)，那么编译器会为之生成一个默认的拷贝构造函数。其形式上，类似于\verb|inline constexpr B(const B &) noexcept = default;|，这个构造函数C++称之为trivial的，编译器可以很放心地将b的副本存储在栈上，此时无需申请堆内存。

由于这个现象高度依赖于编译器的种类、版本等，所以需要具体问题具体分析，不能一概而论。

一般来说，只要手动给出一个拷贝构造函数的定义，只要不是\verb|=default|的，哪怕只是一个空的大括号\verb|{}|什么都不做，编译器也会认为这个构造函数是non-trivial的。

\section{算法}
C++ Primer Plus (6th edition)原书第713页，第16.6节

STL包含很多处理容器的非成员函数。

对于算法函数设计，有两个共同点。

\begin{enumerate}
\item 都使用模板提供泛型
\item 都使用迭代器来提供访问容器中数据的实现方式。
\end{enumerate}

\subsubsection{string类和STL的关系}
\verb|string|类不是STL的组成部分，但是它设计时考虑到了STL思想。

\subsubsection{函数和容器方法}
有时既可以使用STL方法，又可以使用STL函数。使用STL方法一般是更好的选择，因为它对于特定的容器优化得更好。

但是使用STL函数，往往具有更高的通用性、扩展性，因为它可以用于数组、\verb|string|对象、STL容器或者他们的混合体。

\subsection{算法的分类}
STL将算法库分为4组
\begin{enumerate}
\item 非修改式序列操作，对区间中每个元素进行操作，不修改容器的内容
\item 修改式序列操作，操作每个元素而且修改内容
\item 排序和相关操作
\item 通用数字运算，包括累加、乘机、求和、求差等
\end{enumerate}

前三组在头文件\verb|algorithm|中，第四组在\verb|numeric|中，它专用于处理数值数据。

\subsection{通用特征}

\subsubsection{就地算法与复制算法}

对算法进行分类的方法之一，是按结果放置的位置进行分类。有些算法就地完成工作，有些则会创建拷贝。

例如\verb|sort()|函数完成时，结果被存放在原始数据的位置上，因此它是\textbf{就地算法(in-plcae algorithm)}

\verb|copy()|函数将结果发送到另一个位置，所以它是\textbf{复制算法(copying algorithm)}

有些算法有两个版本，就地版本和复制版本。\verb|transform()|函数可以按这两种方式工作。

STL的规定是，复制版本的名称将以\verb|_copy|结尾。

\subsubsection{\_if结尾的算法函数}

根据将函数应用于容器元素之后得到的结果来执行操作，这些版本的名称通常以\verb|_if|结尾。

例如下面的代码示例，将函数用于容器的旧值，如果返回的值为\verb|true|时，将\verb|replace_if()|将把旧值替换为新的值。

\begin{lstlisting}
template<class ForwardIterator, class Predicate class T>
void replace_if(ForwardIterator first, ForwardIterator last,
                Predicate pred, const T & new_value);
\end{lstlisting}

\subsection{遍历}

\subsubsection{for\_each}

\begin{lstlisting}
template< class InputIt, class UnaryFunction >
UnaryFunction for_each( InputIt first, InputIt last, UnaryFunction f );    
\end{lstlisting}

函数对象f的函数特征标(function signature)应该等价于\verb|void fun(const Type &a);|，而且\verb|const &|也并不是必须的，输入对象的类型\verb|InputIt|必须能够隐式转换为这里的Type。

\subsubsection{all\_of, any\_of, none\_of}

\begin{lstlisting}
template< class InputIt, class UnaryPredicate >
bool all_of( InputIt first, InputIt last, UnaryPredicate p );  // C++11起
template< class InputIt, class UnaryPredicate >
bool any_of( InputIt first, InputIt last, UnaryPredicate p );  // C++11起
template< class InputIt, class UnaryPredicate >
bool none_of( InputIt first, InputIt last, UnaryPredicate p );  // C++11起
\end{lstlisting}

\verb|std::all_of|检验一元谓词p是否对给定的范围内所有的元素都返回true

\verb|std::any_of|检验一元谓词p是否对给定的范围内至少一个元素返回true

\verb|std::none_of|检验一元谓词p是否不对范围内的任何元素返回true

\subsection{计数}

\begin{lstlisting}
template< class InputIt, class T >
typename iterator_traits<InputIt>::difference_type count( InputIt first, InputIt last, const T& value );
template< class InputIt, class UnaryPredicate >
typename iterator_traits<InputIt>::difference_type count_if( InputIt first, InputIt last, UnaryPredicate p );
\end{lstlisting}

返回给定范围内，满足特定判定标准的元素的个数

等于给定值的元素，属于满足判定标准

也可以用计数谓词p来表示判定标准，对于经过谓词检验其结果为true的元素进行计数

\subsection{查找与搜索}

\subsubsection{find, find\_if, find\_if\_not}

返回给定范围内，满足特定判定标准的首个元素的迭代器。如果没有找到，则返回last

\verb|find|的判定标准是判断是否等于某个值(operator==)

\verb|find_if|的判定标准是谓词p的结果为true的元素

\verb|find_if_not|的判定标准是谓词p的结果为false的元素

\subsubsection{search, find\_end}

\verb|search|对于给定范围，搜索首次出现某个给定的元素序列的位置

\verb|find_end|查找某个给定的元素序列最后一次出现的位置

运算符==或者二元谓词p

\subsection{比较}

\verb|std::equal|比较两个给定的(容器的)范围是否相等，判断的原则是里面每一个元素都对应相等。

\verb|std::mismatch|返回两个给定的(容器的)范围中，首次出现不相同的元素的位置处的两个元素，存放在一个\verb|std::pair|中返回

\verb|std::lexicographical_compare|按照字典序比较两个范围(比较ASCII码)，返回true或false

(C++20)\verb|std::lexicographical_compare_three_way|用三路比较比较两个范围

\subsection{排序}
头文件\verb|<algorithm>|

\verb|std::sort|按升序排序，用\verb|operator<()|比较元素。时间复杂度为$O(N\log N)$

\verb|std::stable_sort|，时间复杂度为$O(N\log N)$，对于相等的数据，并不会更改其相对顺序，原来在前面的，排序完也在前面。稳定排序需要一些额外的空间复杂度。

\verb|std::nth_element|指定一个位置，只将容器中这个位置的元素排好序，他处于如果全部排序之后的状态，这个位置变成了如果全部排好序后应该是的那个元素，而其他的位置的元素并不改变，保持原状

\verb|std::partial_sort|可以对序列进行部分排序。它需要一个起始迭代器、一个结束迭代器和一个比较函数，将序列排序到给定的位置。它使用快速排序或堆排序，平均时间复杂度为$O(N\log N)$。注意，\verb|std::partial_sort()|不保证稳定性。

\verb|std::partial_sort|是将给定的范围变成，假设按照整个容器全体元素进行排序的情况下这个范围内应有的元素，而给定的范围之外的元素，并不进行排序。

\subsubsection{std::sort()的实现原理}
易知，快速排序的效率，取决于pivot的选择，即，将原始数据划分为2个部分，比pivot大的放在一侧，比pivot小的放在另一侧，这也是快速排序的基本思想。pivot选择得好，则效率更接近理想情况。不当的枢轴选择，导致不当的分割，会使快速排序恶化为 $O(n^2)$

\verb|std::sort()|采用的做法称为median-of-three，即取整个序列的首、尾、中央三个地方的元素，以其中值作为pivot。

分割的方法通常采用两个迭代器head和tail，head从头端往尾端移动，tail从尾端往头端移动，当head遇到大于等于pivot的元素就停下来，tail遇到小于等于pivot的元素也停下来，若head迭代器仍然小于tail迭代器，即两者没有交叉，则互换元素，然后继续进行相同的动作，向中间逼近，直到两个迭代器交叉，结束一次分割。

\verb|std::sort()|收到待排序数据后，先进行内省式排序。内省式排序中，分别用到了\textbf{堆排序}和\textbf{快速排序}。

内省式排序结束后，待排序数据已经基本有序，所以调用一次\textbf{插入排序}，至此，\verb|std::sort()|函数结束。

\subsubsection{内省式排序(Introsort)}
David R.Musser于1996年提出一种混合式排序算法：Introspective Sorting(内省式排序)，简称IntroSort，其行为大部分与上面所说的median-of-three Quick Sort完全相同，但是当分割行为有恶化为二次方的倾向时，能够自我侦测，转而改用堆排序，使效率维持在堆排序的 $O(n\lg n)$，又比一开始就使用堆排序来得好。

\begin{enumerate}
    \item 首先判断元素规模是否大于阀值\verb|__stl_threshold|，\verb|__stl_threshold|是一个常整形的全局变量，值为16，表示若\textbf{元素规模小于等于16}，则结束内省式排序算法，返回sort函数，改用插入排序。
    \item \textbf{若元素规模大于16}，则判断递归调用深度是否超过限制。若已经到达最大限制层次的递归调用，则改用堆排序。代码中的\verb|partial_sort|即用堆排序实现。
    \item 若没有超过递归调用深度，则调用函数\verb|__unguarded_partition()|对当前元素做一趟快速排序，并返回枢轴位置。
\end{enumerate}

\subsection{堆操作(heap operation)}
\begin{enumerate}
    \item \verb|make_heap()|
    \item \verb|push_heap()|
    \item \verb|pop_heap()|
    \item \verb|sort_heap()|
    \item (C++11)\verb|is_heap()|
    \item (C++11)\verb|is_heap_until()|
\end{enumerate}

\subsubsection{make\_heap}
\verb|make_heap()|，建立一个堆结构。

默认建立的是大顶堆(父节点大于等于子节点)，采用默认比较函数是\verb|operator<|或者\verb|std::less{}|(C++20之后)
\begin{lstlisting}
template< class RandomIt >
void make_heap( RandomIt first, RandomIt last );
template< class RandomIt, class Compare >
void make_heap( RandomIt first, RandomIt last, Compare comp );
\end{lstlisting}

\subsubsection{push\_heap}
\verb|push_heap()|，作用是向当前的堆(\verb|[first, last - 1)|范围)中插入一个元素，这个待插入的元素放在迭代器\verb|last|位置处，

使用\verb|push_heap()|必须确保范围\verb|[first, last - 1)|已经是一个堆结构，如果不是的话，会发生未定义行为UB。
\begin{lstlisting}
template< class RandomIt >
void push_heap( RandomIt first, RandomIt last );
template< class RandomIt, class Compare >
void push_heap( RandomIt first, RandomIt last, Compare comp );
\end{lstlisting}

\subsubsection{pop\_heap}
\verb|pop_heap()|，移出堆的堆顶元素，把它放在迭代器指示范围的末尾。调用结束后，\verb|last|会向前移动1个位置。

调用结束后，堆顶元素并未被取走或者删除，他还在原来容器的最末尾位置。

\subsubsection{sort\_heap}
堆排序。

采用默认比较函数是\verb|operator<|或者\verb|std::less{}|(C++20之后)

\subsubsection{(C++11)is\_heap}
判断给定迭代器的范围是否是一个堆结构，默认判定是大根堆。

\subsubsection{(C++11)is\_heap\_until}
判断给定的迭代器的范围中，最大到哪个位置能构成一个堆结构。

返回一个迭代器。
\begin{lstlisting}
template< class RandomIt >
RandomIt is_heap_until( RandomIt first, RandomIt last );
\end{lstlisting}

\subsection{二分查找(对于有序序列)}
一般基于已经排序了的容器

\begin{lstlisting}
template< class ForwardIt, class T >
bool binary_search( ForwardIt first, ForwardIt last, const T& value );    
\end{lstlisting}

\verb|std::binary_search|返回是否查找到，true或false

\verb|lower_bound|返回指向第一个不小于给定值的元素的迭代器

\verb|upper_bound|返回指向第一个大于给定值的元素的迭代器

\verb|equal_range|返回匹配特定键值的元素范围，比如想要查找5，那么就返回第一个5和最后一个5的这个范围的迭代器

\subsection{集合运算(对于有序序列)}

\verb|includes|返回一个序列是否是另一个序列的子集，即包含它的所有元素。仅仅是子集即可，说明不需要是连续而且对应相等，注意与上面的搜索功能区分。

\begin{lstlisting}
template< class InputIt1, class InputIt2 >
bool includes( InputIt1 first1, InputIt1 last1, InputIt2 first2, InputIt2 last2 );
\end{lstlisting}

\subsection{最大最小值}
\begin{enumerate}
    \item \verb|max|给定两个对象，返回其中较大的那一个
    \item \verb|max_element|给定给一个范围，返回其中较大的那一个的迭代器
    \item \verb|min|
    \item \verb|min_element|
\end{enumerate}

\subsection{累加}
头文件\verb|<numeric>|

\begin{lstlisting}
template< class InputIt, class T >
T accumulate( InputIt first, InputIt last, T init );

template< class InputIt, class T, class BinaryOp >
T accumulate( InputIt first, InputIt last, T init, BinaryOp op );
\end{lstlisting}

\verb|first|和\verb|last|分别表示初始位置和终止位置，左闭右开。

\verb|T init|表示累加结果的初值。

\verb|BinaryOp op|是一个二元运算符，他就是执行累加的过程。可以根据需要，把累加替换成别的运算，比如累乘，或者更复杂的内容。

\subsubsection{cppreference中的介绍}
Initializes the accumulator \verb|acc| (of type \verb|T|) with the initial value \verb|init| and then modifies it with \verb|acc = op(acc, *i)|(until C++20)\verb|acc = op(std::move(acc), *i)|(since C++20) for every iterator \verb|i| in the range \verb|[first, last)| in order.

可见，二元运算符\verb|op0|会被以\verb|op(std::move(acc), *i)|的形式来使用。

\subsubsection{代码示例}
在代码示例\ref{std::accumulate代码示例}中，共展示了4种\verb|std::accumulate()|的使用示例。

第二种情况，使用\verb|std::minus<int>()|作为二元运算符。

第三种情况，使用\verb|myfunction|作为二元运算符。

第四种情况，使用的是一个重载了\verb|()|运算符的结构体\verb|myclass|，作为其二元运算符。

\begin{code_example}\label{std::accumulate代码示例}
\begin{lstlisting}
#include <iostream>     // std::cout
#include <functional>   // std::minus
#include <numeric>      // std::accumulate

int myfunction (int x, int y) {return x+2*y;}
struct myclass {
    int operator()(int x, int y) {return x+3*y;}
} myobject;

int main () {
    int init = 100;
    int numbers[] = {10,20,30};
    
    std::cout << "using default accumulate: ";
    std::cout << std::accumulate(numbers,numbers+3,init);
    std::cout << '\n';
    
    std::cout << "using functional's minus: ";
    std::cout << std::accumulate (numbers, numbers+3, init, std::minus<int>());
    std::cout << '\n';
    
    std::cout << "using custom function: ";
    std::cout << std::accumulate (numbers, numbers+3, init, myfunction);
    std::cout << '\n';
    
    std::cout << "using custom object: ";
    std::cout << std::accumulate (numbers, numbers+3, init, myobject);
    std::cout << '\n';
    
    return 0;
}
\end{lstlisting}
\end{code_example}

程序输出
\begin{lstlisting}
160
40
220
280
\end{lstlisting}

\section{其他库}

C++ Primer Plus (6th edition)原书第720页，第16.7节

头文件\verb|complex|提供类模板\verb|complex|，包含用于\verb|float|、\verb|long|、\verb|long double|的具体化，提供标准的复数运算，和能够处理复数的标准函数。

C++11新增的\verb|random|提供了更多的随机数功能。

\subsection{vector、valarray和array}

\verb|vector|是STL的一部分，更侧重于容器功能。

\verb|valarray|类模板是面向数值计算的，不是STL的一部分。

\verb|array|是为了提到内置的数组而设计，让数组更紧凑、效率更高。\verb|array|表示长度固定的数组，因此不支持\verb|push_back()|和\verb|insert()|，但是支持\verb|begin()|、\verb|end()|、\verb|rbegin()|、\verb|rend()|

\verb|valarray|类模板，支持与数值进行四则运算。提供方法\verb|sum()|，计算所有元素的和。\verb|size()|，返回元素个数。\verb|min()|和\verb|max()|

\verb|valarray<bool> vbool = numbers > 9;|，下面的语句将创建一个\verb|bool|数组，\verb|numbers|是一个\verb|valarray<double>|对象，其中\verb|vbool[i]|被设置为\verb|number[i] > 9|的值，\verb|true|或\verb|false|

\verb|slice|类对象可以用作\verb|valarray|类的数组索引，他表示不是一个值，而是多个值。例如\verb|slice(1, 4, 3)|创建的对象表示选择4个元素，它们的索引分别是1、4、7和10

\subsection{(C++11)模板initializer\_list}
详见\ref{initializer_list}

\subsection{使用initializer\_list}

C++ Primer Plus (6th edition)原书第725页，第16.7.3节

要在代码中使用\verb|initializer_list|，必须包含头文件\verb|initializer_list|

\part{其他机制}
\chapter{异常(exception)}
\section{传统C语言如何处理异常}
\subsubsection{什么是异常}
程序运行阶段时遇到错误，导致程序无法正常运行下去。

比如，试图打开一个不可用的文件，请求过多的内存，收到了无法处理的值，0作除数，等等。

\subsubsection{abort()函数}
\verb|abort()|函数位于头文件\verb|cstdlib|或者\verb|stdlib.h|中，它的基本功能是，向标准输出流发送消息abnormal program termination，表示程序异常终止，然后终止该程序。

在某个函数中调用了\verb|abort()|函数，程序不会退回到上一级调用函数，而是直接退出，终止程序。

\subsubsection{错误码}
使用函数的返回值，来处理异常问题。通过不同的返回值，来记录和表达程序是否发生了异常、程序发生了哪一种异常。

比起调用\verb|abort()|函数只有2种状态(不发生异常程序继续运行，发生异常程序退出)来比，返回错误码的方式，更灵活。

\section{C++的异常机制}
\subsection{异常处理的过程}
C++提供对异常进行处理的方法，主要包括以下3个步骤。
\begin{enumerate}
\item 引发异常。(注意，不是发生异常，必须是程序员预先设计过了异常才会进入接下来的步骤进行处理。这里的区别后文还会叙述。)
\item 使用处理程序捕获异常
\item 使用try代码块
\end{enumerate}

\paragraph{引发异常}
使用关键字\verb|throw|来引发异常。

引发异常之后，程序会沿着函数调用序列后退，直至找到包含\verb|try|代码块的部分。

\paragraph{try代码块}
关键字\verb|try|后面跟着一个由花括号括起来的代码块。在这个代码块内部(包括内部的函数调用)如果遇到了\verb|throw|关键字引导的引发异常的语句，程序才会开始C++的异常处理机制。

如果是在\verb|try|代码块之外遇到了\verb|throw|语句，则默认情况下会调用\verb|abort()|函数，但是这个也是可以修改的，后文会叙述到。

\paragraph{捕获异常}
捕获异常，主要是在\verb|try|代码块后面紧跟着的\verb|catch|代码块(其格式与\verb|try|代码块类似，都是关键字后面紧跟一对花括号)

\verb|catch|语句主要与\verb|throw|语句相配合，接受\verb|throw|语句抛出的一个参数，\verb|catch|语句根据这个参数的类别，分别进入不同的\verb|catch|代码块中运行。这个"匹配、选择一个入口进入"的过程，有一点类似于函数调用。这个过程称为\textbf{捕获异常}

\verb|catch|代码块中可以使用接收到的参数，例如\verb|catch char * str|，则\verb|catch|代码块中可以使用这个字符串\verb|str|

如果\verb|try|代码块中未引发异常，则直接跳过所有的\verb|catch|代码块。

\paragraph{catch代码块}

\verb|catch|代码块的排列顺序应该与派生顺序相反

如果不知道异常的具体类型，但是却想捕获所有异常，则可以使用\verb|catch (...) { statements }|

\subsection{异常规范(exception spcification)}

异常规范(exception spcification)在C++98中添加到标准中，但是在C++11中被摒弃了，在C++17中被彻底删除。

由于已经是被抛弃了的规范，建议仅了解即可。

下面的\verb|throw()|部分就是异常规范的使用案例，他用于函数原型和函数定义中，用来表示"该函数是否会引发异常"

\verb|throw()|中间存放一个类型名type，表示会引发这个类型的异常

\begin{lstlisting}
double harm(double a) throw(bad_thing); // may throw bad_thing exception
double marm(double) throw(); // doesn't throw an exception
\end{lstlisting}

\subsection{noexcept关键字}
关键字\verb|noexcept|，用作说明符时，放在函数原型后面，表示这个函数不会引发异常。如果这样的函数抛出了异常，系统会调用\verb|terminate|直接终止进程。

什么时候可以使用\verb|noexcept|？一定不会出现异常的函数。希望提供一个不会失败或者不会抛出异常的函数。

特殊成员函数，自身就携带\verb|noexcept|关键字，包括默认构造、默认拷贝构造、默认赋值、默认移动构造、默认移动赋值。

\verb|noexcept|也可以用作运算符，接受一个函数名，例如，\verb|noexcept(no_excep_func)|，返回\verb|true|或者\verb|false|，表示这个函数是否会引发异常(检查函数原型中是否使用了\verb|noexcept|关键字)

\verb|noexcept|可以用作运算符，接收一个布尔类型的参数，该参数必须是一个常量表达式，决定函数是否抛出异常，默认是\verb|true|。\verb|noexcept(false)|表达的含义是，这个函数可能发生异常。

注意，不写\verb|noexcept|，只能表示程序员既没有说这个函数会报告异常，也没有说它一定不会报告异常。

\begin{lstlisting}
void f1() noexcept(true);
void f1() noexcept(false);
\end{lstlisting}

\subsubsection{嵌套使用noexcept}
下面的代码案例表示，函数\verb|func1()|是否会引发异常，取决于\verb|func2()|是否会引发异常。这种用法实际上来自于\verb|noexcept|作为运算符来使用的情况。
\begin{lstlisting}
void func1(int a, int b) noexcept(noexcept(func2(a, b))) { ... }
\end{lstlisting}

\subsubsection{判断复杂类型的拷贝构造、移动构造是否会抛出异常}
使用\verb|noexcept|来解决移动的过程中抛出异常该怎么办的问题。可以直接在编译期报错，比如
\begin{lstlisting}
static_assert(noexcept(T(std::move(a))))
\end{lstlisting}

\begin{lstlisting}
template <typename T>
T copy(const T& s) noexcept(noexcept(T(s)))
{
    //...
}
\end{lstlisting}

\verb|noexcept|可以判断目标类型的移动构造函数是否可能抛出异常，那么我们可以先判断有没有抛出异常的可能，如果有，那么使用传统的复制操作，那么执行移动构造。

\begin{lstlisting}
template <typename T>
T swap_imp(T& a, T& b, std::integral_constant<bool ,true>) noexcept
{
    //...
}
template <typename T>
T swap_imp(T& a, T& b, std::integral_constant<bool, false>) 
{
    T tmp(a);
    a = b;
    b = tmp;
}

template <typename T>
T swap(T& a, T& b) 
noexcept(noexcept(swap_imp(a,b, std::integral_constant<bool, noexcept(T(std::move(a))) && noexcept(a.operator=(std::move(b)))>())))
{
    swap_impl(a,b, std::integral_constant<bool, noexcept(T(std::move(a))) && noexcept(a.operator=(std::move(b)))>());
}
\end{lstlisting}

\subsection{栈解退(unwinding the stack)}

本节主要解释的问题是，\verb|try|代码块里面的函数里引发了一场，C++是如何处理这些函数一层一层的调用的，是如何回退的直至找到\verb|try|代码块的。

\subsubsection{正常运行时，函数调用是如何执行的}

函数a中在某个语句中，调用了函数b。程序将函数a的指令地址，存放到栈中，转去执行函数b。函数b执行结束后，函数将栈顶的元素弹出，返回到函数a的某个位置处，继续运行。

\subsubsection{引发异常时}

\verb|try|代码块里的函数在一层层调用的过程中，遇到\verb|throw|语句时，也会返回上一层函数调用，逐层释放暂存到栈里的数据，一直回退到遇到\verb|try|代码块的位置。

这个过程称为\textbf{栈解退}。

换句话说，函数调用过程中，遇到\verb|return|语句和遇到\verb|throw|语句，表现是不同的，主要是退回到的位置不同。\verb|return|语句会回退到调用此函数的位置，而\verb|throw|语句会退回到上一个函数，检查是否有\verb|try|代码块，如果没有则继续回退，直至找到为止。

\subsubsection{throw传递参数的方式}

\verb|throw-catch|是按值传递的，传递的是变量的副本。哪怕是声明为引用，也会强制按值传递。

为什么还写引用呢？因为引用可以适用于派生类。

\subsection{标准异常库}\label{标准异常库}

\subsubsection{exception与stdexcept头文件}

C++提供了\verb|exception|类(头文件\verb|exception|)，用于对异常处理进行一些标准化。可以把他用作其他异常类的基类。

头文件\verb|stdexcept|中定义了更多异常类，他们大多是\verb|exception|类的派生类。

从\verb|exception|类中公有派生出了\verb|logic_error|和\verb|runtime_error|

\verb|logic_error|用于表示一些逻辑上的异常，\verb|runtime_error|用于表示一些只有在运行时才会暴露出来的问题

一般而言，\verb|logic_error|异常表示程序中存在可以通过编程修复的问题，\verb|runtime_error|异常表示存在运行时可能会发生的问题。

\paragraph{logic\_error}

从\verb|logic_error|中派生出了\verb|domain_error|、\verb|invalid_argument|、\verb|length_error|、\verb|out_of_bounds|

函数计算产生了超过值域的值时，引发\verb|domain_error|异常，比如sin函数产生了超过(-1,1)的值时。

函数接受到了一个异常的值，引发\verb|invalid_argument|异常。

空间不足时，比如数组运算等，引发\verb|length_error|异常。

索引错误时，引发\verb|out_of_bounds|异常。

\paragraph{runtime\_error}

从\verb|runtime_error|中派生出了\verb|range_error|、\verb|overflow_error|、\verb|underflow_error|

\subsubsection{new头文件}

使用\verb|new|运算符，如果导致内存分配问题，可以让它引发\verb|bad_alloc|异常。

头文件\verb|new|中包含了\verb|bad_alloc|类的声明，也是从\verb|exception|派生而来的

在C++标准里采用这些异常之前，使用\verb|new|运算符如果无法分配所请求的内存时，会给\verb|new|返回一个空指针。

由于异常机制是晚于"请求失败则返回空指针"这个思路的，所以为了兼容旧的代码，C++提供了\verb|std::nothrow|

\begin{lstlisting}
Big * pb;
pb = new (std::nothrow) Big[10000]; // 1,600,000,000 bytes
if (pb == 0)
{
    cout << "Could not allocate memory. Bye.\n";
    exit(EXIT_FAILURE);
}
\end{lstlisting}

\subsection{typeinfo头文件}
\verb|typeinfo|头文件里包含了\verb|bad_cast|异常，用于\verb|dynamic_cast|运算符引发的异常。

如果传给\verb|typeid|运算符一个对空指针取值的表达式，则会发出\verb|bad_typeid|的异常。

\subsubsection{typeinfo}\label{typeinfo}
\verb|#include <typeinfo>|

\begin{enumerate}
    \item \verb|typeid|是c++的关键字之一
    \item \verb|typeid|操作符的返回结果是名为\verb|type_info|的标准库类型的对象的引用(在头文件\verb|typeinfo|中定义)    
    \item \verb|typeid|只能在运行时起作用，无法在编译期起作用。
    \item C++并没有规定\verb|typeid|的实现标准，因此各个编译器的表现可能不一样
    \item 编译器会为每一种\verb|typeid|操作的类型生成一份保存在数据段的\verb|type_info|数据
    \item 每种类型的\verb|type_info|数据长度依赖于类型名称，至少9个字节。
\end{enumerate}

\subsubsection{判断两个变量类型是否相同}

\begin{lstlisting}
#include <iostream>   // std::cout
#include <typeinfo>   // operator typeid

struct Base {};
struct Derived : Base {};
struct Poly_Base {virtual void Member(){}};
struct Poly_Derived: Poly_Base {};

typedef int my_int_type;

int main() {
    std::cout << std::boolalpha;
    
    // fundamental types:
    std::cout << "int vs my_int_type: ";
    std::cout << ( typeid(int) == typeid(my_int_type) ) << '\n';
    
    // class types:
    std::cout << "Base vs Derived: ";
    std::cout << ( typeid(Base)==typeid(Derived) ) << '\n';
    
    // non-polymorphic object:
    Base* pbase = new Derived;
    
    std::cout << "Base vs *pbase: ";
    std::cout << ( typeid(Base)==typeid(*pbase) ) << '\n';
    
    // polymorphic object:
    Poly_Base* ppolybase = new Poly_Derived;
    
    std::cout << "Poly_Base vs *ppolybase: ";
    std::cout << ( typeid(Poly_Base)==typeid(*ppolybase) ) << '\n';
    
    return 0;
}   
\end{lstlisting}

运行结果

\begin{lstlisting}
int vs my_int_type: true
Base vs Derived: false
Base vs *pbase: true
Poly_Base vs *ppolybase: false
\end{lstlisting}

\subsubsection{name()函数}
获取数据类型的名字，这个名字是C风格的字符串指针

\begin{lstlisting}
#include <iostream>       // std::cout
#include <typeinfo>       // operator typeid

struct Base {};
struct Derived : Base {};
template<class T>
void swap(T a, T b)
{
    std::cout << "T is: " << typeid(T).name() << '\n';
    T temp = a;
    a = b;
    b = temp;
}

int main() {
    int i;
    int * ptr;
    std::cout << "int is: " << typeid(int).name() << '\n';
    std::cout << "  i is: " << typeid(i).name() << '\n';
    std::cout << " pi is: " << typeid(ptr).name() << '\n';
    std::cout << "*pi is: " << typeid(*ptr).name() << '\n';
    
    Base base;
    
    std::cout << "Base is: " << typeid(Base).name() << '\n';
    std::cout << "base is: " << typeid(base).name() << '\n';
    std::cout << "Derived is: " << typeid(Derived).name() << '\n';
    
    swap(1,2);
    
    return 0;
}
\end{lstlisting}

运行结果

\begin{lstlisting}
int is: i
i is: i
pi is: Pi
*pi is: i
Base is: 4Base
base is: 4Base
Derived is: 7Derived
T is: i
\end{lstlisting}

\subsection{意外异常与未捕获异常}
\subsubsection{未捕获异常(uncaught exception)}

没有在\verb|try|代码块中触发但是抛出了的异常，成为未捕获异常

\paragraph{处理方式}

默认情况下，程序会调用\verb|terminate()|函数，而在这个函数之中会调用\verb|abort()|函数。

用户可以通过制定\verb|terminate()|中运行的函数来修改这个默认流程。通过\verb|set_terminate()|来配置\verb|terminate()|中运行的函数。

\verb|set_terminate()|和\verb|terminate()|都在头文件\verb|exception|中声明

\begin{lstlisting}
typedef void (*terminate_handler)();
terminate_handler set_terminate(terminate_handler f) throw(); // C++98
terminate_handler set_terminate(terminate_handler f) noexcept; // C++11
void terminate(); // C++98
void terminate() noexcept; // C++11
\end{lstlisting}

\verb|typedef void (*terminate_handler)();|的含义是，将\verb|terminate_handler|声明为一个，指向没有参数和返回值的函数的指针。

\verb|set_terminate()|接收一个，不带任何参数而且返回类型为\verb|void|的函数的函数名(即函数开头的地址)，作为其参数。

如果\verb|set_terminate()|调用了多次，则\verb|terminate()|以最后一次为准。

\subsubsection{意外异常(unexcepted exception)}

在\verb|try|代码块中触发，但是未找到匹配的\verb|catch|块

\paragraph{处理方式}

与未捕获异常类似，有对应的\verb|unexpected()|和\verb|set_unexpected()|函数，也是在头文件\verb|exception|中声明

\begin{lstlisting}
typedef void (*unexpected_handler)();
unexpected_handler set_unexpected(unexpected_handler f) throw(); // C++98
unexpected_handler set_unexpected(unexpected_handler f) noexcept; // C++11
void unexpected(); // C++98
void unexpected() noexcept; // C+0x
\end{lstlisting}


\chapter{RTTI}
RTTI是运行阶段类型识别(Runtime Type Identification)的简称

这是新添加到C++中的特性之一，很多老式实现不支持。另一些实现可能需要编译器在配置上开启RTTI功能才能支持。

\subsubsection{用途}
假设有一个类层次结构，其中的类都是从一个基类派生而来的，则可以让基类指针指向其中任何一个类的对象。

有时候我们会想要知道指针具体指向的是哪个类的对象。可能希望调用类方法的正确版本，而有时候派生对象可能包含不是继承而来的方法，此时，只有某些类的对象可以使用这种方法。

也可能是出于调试目的，想跟踪生成的对象的类型。

\section{用法}
C++提供3个支持RTTI的元素
\begin{enumerate}
    \item \verb|dynamic_cast|运算符
    \item \verb|typeid|运算符
    \item \verb|type_info|结构
\end{enumerate}

\subsection{dynamic\_cast运算符}
\subsubsection{用法}
\verb|dynamic_cast|运算符并不直接回答"该指针指向的对象时哪类对象"的问题，但是能回答"是否可以安全地将对象的地址赋给某个给定类型的指针"的问题。
\begin{lstlisting}
dynamic_cast<type>(expression)
\end{lstlisting}

\verb|dynamic_cast|专门用于将多态基类的指针或引用强制转换为派生类的指针或引用，而且能够检查转换的安全性。对于不安全的指针转换，转换结果返回空指针\verb|nullptr|。这一点与\verb|reinterpret_cast|有区别，他不检查转换安全性。

\begin{code_example}\label{dynamic_cast运算符代码示例1}
\begin{lstlisting}
class Grand { // has virtual methods};
class Superb : public Grand { ... };
class Magnificent : public Superb { ... };

Grand * pg = new Grand;
Grand * ps = new Superb;
Grand * pm = new Magnificent;

Magnificent * p1 = (Magnificent *) pm; // #1
Magnificent * p2 = (Magnificent *) pg; // #2
Superb * p3 = (Magnificent *) pm; // #3
\end{lstlisting}
\end{code_example}

转换\verb|#1|是安全的，派生类对象的地址赋值给派生类指针，这是向上转换(upcasting)。而转换\verb|#2|不安全，因为它将基类对象的地址，赋值给派生类指针，这是向下转换(downcasting)。在第\ref{指针和引用的兼容性}节中，我们提到过向上转换和向下转换。向下转换是不安全的。

转换\verb|#3|是安全的，因为它将派生类对象的地址，复制给基类指针。公有派生关系，确保这里的\verb|Magnificent|对象同时既是一个\verb|Superb|对象(直接基类)也是一个\verb|Grand|对象(间接基类)。

如果在类型转换时使用\verb|dynamic_cast|运算符，那么可以看下面的代码示例。这个表达式，判断指针\verb|pg|的类型是否可以被安全地转换为\verb|Superb *|，如果可以，等于号右边的表达式将返回对象的地址，否，则返回一个空指针。

\begin{lstlisting}
Superb * pm = dynamic_cast<Superb *>(pg);
\end{lstlisting}

下面的代码示例，如果类型转换成功，则\verb|ps|的值非零，非零则是\verb|true|。

如果类型转换失败，则不会运行下面的语句\verb|ps->Say();|
\begin{lstlisting}
if (ps = dynamic_cast<Superb *>(pg)) {
    ps->Say();
}
\end{lstlisting}

\paragraph{dynamic\_cast与引用}
对于指针，如果转换失败会返回\verb|nullptr|，这可以用于\verb|if|语句进行判断。

但是对于涉及到与引用有关的转换的情况，由于没有与空指针相对应的"空引用"这种东西。C++作出这样的设计，当引用的转换不正确时，将引发\verb|bad_cast|的异常，详见第\ref{标准异常库}节对\verb|bad_cast|异常的介绍，以及\verb|typeinfo|头文件。通过处理异常，就能发现不安全的转换。

\subsection{typeid运算符}
\verb|typeid|运算符能够确定两个对象是否为同一种类型，可以接受两种参数，类名，或结果为对象的表达式。

\begin{lstlisting}
typeid ( type )
typeid ( expression )	    
\end{lstlisting}

\verb|typeid|表达式，是一个左值表达式。

\verb|typeid|运算符返回一个对\verb|std::type_info|对象的引用，后者是在头文件\verb|typeinfo|里定义的。

\subsubsection{不关注cv限定符}
\verb|typeid|运算符并不会关注cv限定符，即\verb|typeid(T) == typeid(const T)|

\subsubsection{大部分情况，编译期完成求值}
\subsubsection{少部分情况有运行时开销}
在应用于带有多态机制的表达式时，\verb|typeid|表达式的求值，可能带来一些运行时开销，因为涉及虚表查找。其他情况下，\verb|typeid|表达式都在编译期就完成了求值。

\subsubsection{在构造和销毁过程中使用typeid运算符}
cppreference中有如下叙述

在构造和析构过程中使用\verb|typeid|运算符，包括构造函数的初始化列表和默认成员的初始化器，那么此时\verb|typeid|运算符所引用的\verb|std::type_info|对象，表示正在构造和析构的对象，尽管他可能不是最终的派生类(即，可能处于派生链中，处于递归调用上层基类构造函数的过程之中)。

If \verb|typeid| is used on an object under construction or destruction (in a destructor or in a constructor, including constructor's initializer list or default member initializers), then the \verb|std::type_info| object referred to by this typeid represents the class that is being constructed or destroyed even if it is not the most-derived class.

而在除此之外的其他上下文中，这样使用\verb|typeid|运算符会带来未定义行为UB。

In other contexts, evaluating such an expression results in undefined behavior.

\subsubsection{不保证type\_info对象是完全一致的}
对于同一类型使用\verb|typeid|运算符，并不保证都引用同一个\verb|std::type_info|对象，不过这些对象的\verb|std::type_info::hash_code|是相同的，它们的 \verb|std::type_index| 也相同。

cppreference给出了下面的代码示例
\begin{lstlisting}
const std::type_info& ti1 = typeid(A);
const std::type_info& ti2 = typeid(A);

assert(&ti1 == &ti2); // 不保证
assert(ti1 == ti2); // 保证
assert(ti1.hash_code() == ti2.hash_code()); // 保证
assert(std::type_index(ti1) == std::type_index(ti2)); // 保证
\end{lstlisting}

\subsection{type\_info类}
\verb|std::type_info|类是虚析构函数，使得它可以成为虚基类。不能拷贝赋值。

\verb|std::type_info|类重载了\verb|==|和\verb|!=|运算符，可以使用它们进行比较，类型相同或者类型不同。

\verb|name()|方法，返回一个存储着类名的字符串，更多细节与编译器的具体实现有关，并不是统一的。

\verb|before()|方法，检查在实现定义的顺序中，被指代类型是否在另一个 \verb|type_info| 对象之前，即对被指代类型排序。

\verb|hash_code()|方法，返回对所有指代同一类型的 \verb|std::type_info| 对象都相同的值。

\section{类型转换运算符}\label{类型转换运算符}
\subsubsection{适用背景}
传统的C语言中，对于类型转换，都是使用赋值运算符\verb|=|来实现的。这种相对自由松散的设计方式，使得看上去同样的表达式可以实现许多种不同的赋值过程。

Bjarne Stroustrup提出了一种思路，用更严格的方式来限制类型转换，并添加4种类型转换运算符，使得转换的过程更加规范，而且显而易见地表达编程者的意图。

\subsection{const\_cast运算符}
用法与\verb|dynamic_cast|运算符相同。
\begin{lstlisting}
const_cast<type>(expression)
\end{lstlisting}

其中，\verb|type_name|必须是指针、引用或者成员指针类型。

其功能是去除类型中的\verb|const|和\verb|volatile|属性，并不修改其他任何内容。

\subsubsection{去掉const修饰符}
下面的代码示例中，直接通过\verb|rci = 100|修改\verb|rci|是不行的，因为他带有\verb|const|。

通过\verb|const_cast<int &>(rci)|可以将\verb|const int &|转换为\verb|int &|
\begin{lstlisting}
int i = 3; // 不声明 i 为 const
const int &rci = i;
// rci = 100;  //错误
const_cast<int &>(rci) = 4; // OK：修改 i
\end{lstlisting}

\subsubsection{去掉类的const成员函数的const属性}
\verb|const_cast|另外一种使用场景就是：在使用第三方库或API时，它们只提供了非\verb|const|类型的参数的函数，但我们只有\verb|const|类型的对象。

下面的代码示例中，直接使用\verb|this->i = v;|是不行的，因为函数\verb|f()|是\verb|const|成员函数。所以必须把\verb|this|转换为\verb|type *|
\begin{lstlisting}
struct type {
    int i;
    
    type(): i(3) {}
    
    void f(int v) const
    {
        // this->i = v;                 // 编译错误：this 是指向 const 的指针
        const_cast<type *>(this)->i = v; // 只要该对象不是 const 就 OK
    }
};

type t; // 如果这是 const type t，那么 t.f(4) 会是未定义行为
t.f(4);
\end{lstlisting}

\subsubsection{去除volatile属性}
通过\verb|const_cast|将\verb|b1|的类型由\verb|volatile int*|转换为 \verb|int*|。

从输出结果可以看到，b1的\verb|typeid|为PVi(pointer to a volatile integer，指向\verb|volatile|类型的\verb|int|指针)，c1的\verb|typeid|为Pi(Pointer to integer，指向\verb|int|的指针)，使用\verb|const_cast|去除了\verb|b1|的\verb|volatile|的属性。
\begin{lstlisting}
int a1 = 40;
volatile int* b1 = &a1;
cout << "typeid of b1 " << typeid(b1).name() << '\n';
int* c1 = const_cast<int*>(b1);
cout << "typeid of c1 " << typeid(c1).name() << '\n';
\end{lstlisting}

输出结果
\begin{lstlisting}
typeid of b1 PVi
typeid of c1 Pi
\end{lstlisting}

\subsubsection{产生未定义行为UB的使用案例}
\begin{lstlisting}
//未定义的行为，不提倡使用
const int j = 3; // 声明 j 为 const
int *pj = const_cast<int *>(&j);
*pj = 4; // 未定义行为
std::cout << "j = " << j << " ,addr(j):" << &j << '\n';
std::cout << "*pj = " << *pj << " ,addr(*pj):" << pj << '\n';

//正常的行为
int j1 = 3;//最初声明为非const
const int *cpj1  = &j1;
int *pj1 = const_cast<int *>(cpj1);//cpj1最终指向的值(即j1的值)为非const类型，可以使用const_cast
*pj1 = 4;
std::cout << "j1 = " << j1 << " ,addr(j1):" << &j1 << '\n';
std::cout << "*pj1 = " << *pj1 << " ,addr(*pj1):" << pj1 << '\n';
\end{lstlisting}

运行结果
\begin{lstlisting}
j = 3 ,addr(j):0x30932680c
*pj = 4 ,addr(*pj):0x30932680c
j1 = 4 ,addr(j1):0x3093267fc
*pj1 = 4 ,addr(*pj1):0x3093267fc
\end{lstlisting}

从运行结果可以看出，\verb|j|和\verb|*pj|的地址相同，\verb|j1|和\verb|*pj1|的地址相同，但是\verb|j|和\verb|*pj|显示的值却不同。

这就是因为编译器优化结果造成的。由于声明\verb|j|的时候声明为了\verb|const int|，所以在编译阶段，编译器认为它就是不变的类型，会将\verb|j|直接替换为常量\verb|3|。

\subsubsection{小结}
\begin{enumerate}
    \item 仅当实际引用的对象/变量不是常量，才使用\verb|const_cast|
    \item 当我们调用第三方库和一些API时，它们需要使用非\verb|const|形式的数据，但我们只有\verb|const|形式数据时候才能使用\verb|const_cast|。
\end{enumerate}

总的来说就是：仅在不得不的情况下使用\verb|const_cast|。

\subsection{static\_cast运算符}
\subsubsection{用法}
仅当\verb|type|可以被隐式地转换为\verb|expression|所属的类型时，才合法。
\begin{lstlisting}
static_cast<type>(expression)
\end{lstlisting}

不能用于移除或添加 \verb|const| 限定符。在这种情况下，应使用 \verb|const_cast|。

\verb|static_cast| 不执行运行时类型检查，所以性能稍高一些。如果对转换不确定，考虑使用其他类型的转换，如 \verb|dynamic_cast|，或者重新评估设计，以避免需要转换。

\subsubsection{优点}
\begin{enumerate}
    \item 相对于 C 风格的强制转换(如\verb|(int)3.14|)，\verb|static_cast| 更加明确和可读。
    \item 与 C 风格的强制转换相比，\verb|static_cast| 只能执行明确允许的转换，这有助于避免一些错误。
\end{enumerate}

\subsubsection{基础数据类型的转换}
\begin{lstlisting}
double d = 5.5;
int i = static_cast<int>(d);  // i = 5
\end{lstlisting}

枚举量\verb|enum|与整型\verb|int|之间
\begin{lstlisting}
enum Color { RED, GREEN, BLUE };
int value = static_cast<int>(GREEN);  // value = 1
\end{lstlisting}

\subsubsection{指向派生类的指针或引用转换为指向基类的指针或引用}
从基类指针转换为派生类指针(向下强制转换downcast)，必须显式进行，否则是不合法的，此时可以使用\verb|static_cast|运算符

\begin{lstlisting}
High bar;
Low blow; // Low : public High
...
High * pb = static_cast<High *> (&blow); // valid upcast
Low * pl = static_cast<Low *> (&bar); // valid downcast
Pond * pmer = static_cast<Pond *> (&blow); // invalid, Pond unrelated
\end{lstlisting}

\subsubsection{与右值引用的联系}
\verb|static_cast|运算符，还在右值引用与移动语义技术中有所应用，主要指的是\verb|std::move|，详情可见第\ref{引用折叠，万能引用，std::move}节，以及代码示例\ref{static_cast与std::move}。

\subsection{reinterpret\_cast运算符}
\subsubsection{用法}
\verb|reinterpret_cast|运算符并不会改变括号中运算对象的值，而是对该对象从位模式上进行重新解释。

\verb|reinterpret_cast| 用于进行各种不同类型的指针之间、不同类型的引用之间以及指针和能容纳指针的整数类型之间的转换。转换时，执行的是逐个比特复制的操作。

\verb|reinterpret_cast|体现了 C++ 语言的设计思想：用户可以做任何操作，但要为自己的行为负责。
\begin{lstlisting}
reinterpret_cast<type>(expression)
\end{lstlisting}

\begin{code_example}\label{reinterpret_cast运算符代码示例1}
\begin{lstlisting}
#include <iostream>
using namespace std;
int main(int argc, char** argv)
{
    // 用16进制表示32位int，0x61是字符'a'的ASCII码
    int num = 0x00636261;  
    int * pnum = &num;
    char * pstr = reinterpret_cast<char *>(pnum);
    cout<<"pnum指针的值: "<<pnum<<endl;
    
    // 直接输出pstr会输出其指向的字符串，这里的类型转换是为了保证输出pstr的值
    cout<<"pstr指针的值: "<<static_cast<void *>(pstr)<<endl;
    
    cout<<"pnum指向的内容: "<<hex<<*pnum<<endl;
    cout<<"pstr指向的内容: "<<pstr<<endl;
    return 0;
}    
\end{lstlisting}
\end{code_example}

在代码示例\ref{reinterpret_cast运算符代码示例1}中，使用\verb|reinterpret_cast|运算符把\verb|pnum|从\verb|int*|转变成\verb|char*|类型并用于初始化\verb|pstr|。

按照16进制，输出\verb|pnum|指向的内容，得到的结果会是\verb|636261|。

而输出\verb|pstr|得到的内容，会是\verb|abc|(abc的ASCII码分别是0x61, 0x62, 0x63)。

\chapter{输入输出与文件}
\section{C++的输入输出理论概述}
\subsection{C++的输入输出理论}
\paragraph{字节流的概念}
C++把输入输出，看做是字节流。每一个字节都要单独解析，而不管这个字节所在的更大的数据结构是什么。C++标准IO不关心比字节更大的数据结构具体是什么，他只是把你要传输的东西按照字节分割好，然后一个字节一个字节的发送出去。如果是接收数据，那么就把接收到的字节按照顺序排列好，每次只接收1个字节。

例如，要发送浮点数-2.34给屏幕，则需要依次发送-、2、.、3、4这5个字符给屏幕，而并非将-2.34先转换为二进制，再按照4bits 4bits的顺序发送。

所谓的"管理输入输出"，主要是做2件事情，将流与输入端连接起来，将流与输出端连接起来。

\paragraph{缓冲区的概念}
缓冲区：用作中介的内存块。

流不会把数据直接发送到输出端，而是先放到缓存区，然后待缓存区满，则将缓存区一整个地传输到输出端，这一过程称为"刷新缓冲区"(flushing the buffer)。也可以主动发出刷新缓冲区的指令，这样可以在缓冲区未满的时候就刷新缓冲区。

\subsection{C++标准I/O类库}
\begin{enumerate}
\item \verb|iostream|和\verb|fstream|是C++负责标准I/O的标准类库

\verb|iostream|里面，包含了\verb|istream|类的\verb|cin|对象，和\verb|ostream|类的\verb|cout|对象

\verb|fstream|是负责管理文件输入输出的头文件，里面包含了\verb|ifstream|类，和\verb|ofstream|类

\item \verb|ios|类，派生出了\verb|iostream|类、\verb|istream|类和\verb|ostream|类。

而后面两者，分别只具有输入和输入的方法。

\item \verb|ios_base|类，派生出的\verb|ios|类，是最底层的一个类，表示流的一般特征，比如是否可读取、是二进制流还是文本流等。

\item 另外还有\verb|streambuff|类，为缓冲区提供了内存，并提供了用于填充缓冲区、访问缓冲区内容、刷新缓冲区和管理缓冲区内存的类方法。
\end{enumerate}

\subsection{重定义I/O}

介绍一下C++标准I/O功能的演变。

C++98中，对I/O做了两方面的修订，首先是从\verb|ostream.h|到\verb|ostream|的变化，用\verb|ostream|将类放到\verb|std|命名空间中。

其次，重新编写了I/O类。

随着计算机软硬件的共同发展，C++必须能够处理16位宽度或更宽的字符类型。因此，以前的8位宽的\verb|char|类型，已经不能满足需要了，在此基础上增加了\verb|wchar_t|字符类型。

而在C++11中，更是增加了\verb|char16_t|和\verb|char32_t|

标准委员会，并没有开发4套独立的类来负责这些不同宽度的字符的标准I/O，而是开发了1套标准I/O类模板。

\begin{enumerate}
\item \verb|basic_istream<charT, traits<charT>>|
\item \verb|basic_ostream<charT, traits<charT>>|
\end{enumerate}

\verb|traits<charT>|模板是一个模板类，为字符类型定义了具体特性，比如如何比较字符是否相等以及字符的EOF值等。

该C++1l标准提供了IO的\verb|char|和\verb|wchar_t|具体化。例如，\verb|istream|和\verb|ostream|都是\verb|char|具体化的\verb|typedef|。同样，\verb|wistream|和\verb|wostream|都是\verb|wchar_t|具体化。例如，\verb|wcout|对象用于输出宽字符流。

在头文件\verb|ostream|中包含了这些定义。

\verb|ios|基类中的一些独立于类型的信息被移动到新的\verb|ios_base|类中,这包括各种格式化常量，例如\verb|ios::fixed|(现在为\verb|ios_base::fixed|)。

另外，\verb|ios_base|还包含了一些老式\verb|ios|中没有的选项。

\subsection{重定向}

输入重定向\verb|<|

输出重定向\verb|>|

\subsection{std::ifstream}
\verb|std::ifstream|是 C++ 标准库中用于文件输入的类，它是\verb|std::basic_ifstream<char>|的一个特化版本。

\paragraph{按行读取}
\begin{lstlisting}
std::ifstream file("example.txt"); // 打开文本文件
if (file.is_open()) {
    while (getline(file, line)) { // 按行读取
        std::cout << line << '\n';
    }
    file.close();
}
\end{lstlisting}

\paragraph{按字符读取}
\begin{lstlisting}
std::ifstream file("example.txt");
char ch;
while (file.get(ch)) { // 每次读取一个字符，直到文件末尾
    std::cout << ch;
}
file.close(); // 关闭文件
\end{lstlisting}

\paragraph{使用>>符号来读取文件}
使用\verb|>>|符号来读取文本文件时，默认情况下会忽略white space
\begin{lstlisting}
std::ifstream file("example.txt");
std::string word;
// 默认会忽略输入流中的空白字符，包括空格、制表符、换行符等
while (file >> word) { // 按空白字符分隔读取每个单词
    std::cout << word << std::endl;
}
file.close();
\end{lstlisting}

\paragraph{读取原始数据}
他提供了2种方法来读取文件的原始数据，\verb|read()|和\verb|readsome()|。区别在于处理缓冲区和返回值的方式不同。

\paragraph{read()}
\verb|read()|尝试从文件中读取指定数量的字符，并将这些字符填充到提供的缓冲区中。它会尝试读取请求的所有字节，即使这需要等待更多的数据到达，也就是说，他可能会阻塞。

\verb|read()| 不返回读取的字符数；相反，它修改了流的状态位。要检查实际读取了多少个字符，可以使用成员函数 \verb|gcount()|。

\paragraph{readsome()}
\verb|readsome()| 函数从文件中读取当前可用的数据，但不会超过请求的数量。它只读取当前已经在流的内部缓冲区中可用的数据，不会阻塞等待更多数据。他也不返回实际读取了多少个字符，也要用\verb|gcount()|。

\paragraph{rdbuf()}
他返回一个指向\verb|std::basic_streambuf|对象的指针。这个流缓冲区对象，与文件流对象相关联，并负责实现输入输出工作。可以直接操作缓冲区数据。

下面的代码示例中，就将\verb|std::ifstream|这个文件流对象的缓冲区，都读取到了\verb|std::stringstream buffer|这个流缓冲区对象中。
\begin{lstlisting}
std::ifstream file("example.txt");
std::stringstream buffer;
buffer << file.rdbuf(); // 读取整个文件内容到buffer
\end{lstlisting}

\paragraph{读取二进制文件}
解读一下下面的代码示例。

以二进制模式打开该文件，并定位到文件末尾。使用\verb|tellg()|获取文件的大小。使用\verb|seekg()|回到文件开头。分配一块内存空间，将文件的二进制数据通过\verb|read()|读取到这块内存空间。

\begin{lstlisting}
int main() {
    // 以二进制模式打开文件
    std::ifstream file("example.bin", std::ios::binary | std::ios::ate); // std::ios::ate 定位到文件末尾，便于获取文件大小
    if (file.is_open()) {
        // 获取文件大小
        std::streamsize size = file.tellg();
        file.seekg(0, std::ios::beg); // 定位回文件开始
        // 分配内存缓冲区
        std::vector<char> buffer(size);
        // 读取文件到缓冲区
        if (file.read(buffer.data(), size)) {
            /* 成功读取后的处理 */
        } else {
            std::cout << "Error reading file";
        }
        file.close();
    } else {
        std::cout << "Unable to open file";
    }
    return 0;
}
\end{lstlisting}

\paragraph{分段读取二进制文件}
\begin{lstlisting}
int main() {
    // 打开一个二进制文件
    std::ifstream file("example.bin", std::ios::binary);
    if (!file) {
        std::cerr << "Unable to open file" << std::endl;
        return 1;
    }
    const size_t bufferSize = 1024; // 定义每次读取的字节数
    char buffer[bufferSize]; // 创建一个缓冲区
    // 循环读取文件，直到到达文件末尾
    while (!file.eof()) {
        file.read(buffer, bufferSize); // 尝试读取下一段数据
        std::streamsize bytes = file.gcount(); // 获取实际读取的字节数
        /// 处理读取到的数据...
        /// 这里只是简单地打印出实际读取的字节数
        std::cout << "Read " << bytes << " bytes" << std::endl;
        /// 如果需要，可以对buffer中的数据进行处理
        /// 判断退出条件(此处可避免多读一次)。
        if(file.peek() == EOF){
            break;
        }
    }
    file.close(); // 关闭文件
    return 0;
}
\end{lstlisting}

\section{使用cout进行输出}

\verb|ostream|类，最主要的任务，是将数值类型转换为以文本形式表示的字符流。

\subsection{重载<<运算符}

\verb|<<|的默认含义是按位左移运算符。重载后，称为插入运算符

\verb|ostream|类提供了\verb|operator<<()|函数的定义

比如，表达式\verb|cout<<88|对应的函数原型是\verb|ostream & operator<<(int);|

返回一个指向\verb|ostream|对象的引用。指向哪个对象呢？根据函数的定义，将指向调用该运算符的对象。

例如，\verb|cout << "test";|返回的就是指向该\verb|cout|对象的引用。

这一特性说明了，插入运算符是可以连续使用的。比如\verb|cout << "we have" << count << "chickens.";|

\subsection{其他ostream方法}

除了各种\verb|operator<<()|函数外，\verb|ostream|类还提供了\verb|put()|方法和\verb|write()|方法

\verb|put()|用于显示字符

\verb|write()|用于显示字符串，并不会在遇到空字符的时候自动停止打印字符串。

\subsection{刷新输出缓冲区}

向硬盘输出，先存放到缓冲区，再一并刷新，即只需要读写一次硬盘即可。

硬盘的读写速度和内存、cache的读写速度，是不可同日而语的，不能像频繁读写内存那样频繁地读写硬盘。

而向屏幕输出，则不同于向硬盘输出的情况。此时，程序不必等缓冲区填满，遇到换行符，即刷新缓冲区。

如果想要提前手动刷新缓冲区，则可以使用控制符\verb|flush|，格式如下

\verb|cout << flush;|

实际上，控制符本质上是一个函数\verb|flush(cout)|，上面的形式，是由于\verb|ostream|类对运算符\verb|<<|进行了重载

\subsection{用cout进行格式化}

\begin{enumerate}
\item 对于\verb|char|值，如果它代表的是可打印字符，则将被作为一个字符显示在宽度为一个字符的字段中。
\item 对于数值整型，将以十进制方式显示在一个刚好容纳该数字及负号(如果有的话)的字段中。
\item 字符串被显示在宽度等于该字符串长度的字段中。
\end{enumerate}

总的来说，输入给\verb|cout|的整型、字符和字符串数据，原来有多长，那么输出的就会有多长，并没有什么变化。

对于浮点数，可能有一点变化。

浮点类型被显示为6位，末尾的0不显示(注意，显示的数字位数与数字被存储时精度没有任何关系)。数字以定点表示法显示还是以科学计数法表示，取决于它的值。具体来说，当指数大于等于6或小于等于-5时，将使用科学计数法表示。

因为每个值的显示宽度都等于它的长度，因此必须显式地在值之间提供空格。否则，相邻的值将不会被分开。

\paragraph{调整计数制，十进制或十六进制}
\verb|ostream|类是从\verb|ios|类派生而来的，而后者是从\verb|ios_base|类派生而来的。\verb|ios_base|类存储了描述格式状态的信息。例如，一个类成员中某些位决定了使用的计数系统，而另一个成员则决定了字段宽度。通过使用控制符(manipulator)，可以控制显示整数时使用的计数系统。通过使用\verb|ios_base|的成员函数，可以控制字段宽度和小数位数。由于\verb|ios_base|类是\verb|ostream|的间接基类，因此可以将其方法用于\verb|ostream|对象(或子代)，如\verb|cout|。

十进制\verb|dec|，十六进制\verb|hex|，八进制\verb|oct|

如果想切换为十六进制，则使用\verb|hex(cout);|或者\verb|cout << hex;|(由于重载了运算符的缘故)，程序将一直以十六进制来打印数值，直至格式状态设置为其他选项为止。

\paragraph{调整字段宽度}
成员函数\verb|width()|

\verb|width()|方法只影响即将显示的下一个项目，不会一直起作用，跟之前提到的\verb|hex|等不同。

\paragraph{填充字段}
填充字符(fill character)

默认右对齐

成员函数\verb|fill()|，会一直起作用，直至设置为其他选项为止。

使用方法，\verb|cout.fill('*');|

\paragraph{设置精度}
成员函数\verb|precision()|，会一直起作用，直至设置为其他选项为止。

默认为6位

\paragraph{补充末尾的0，setf()函数}
\verb|iostream|类没有专门的函数处理这个问题，但是\verb|iso_base|类有\verb|setf()|函数

显示末尾小数点和多余的零\verb|cout.setf(iso_base::showpoint)|

\verb|showpoint|似乎\verb|iso_base|类声明中定义的类级静态变量，所以要加作用域运算符\verb|::|

\section{使用cin进行输入}

\verb|cin|对象根据要接收输入的变量的类型，将输入的字节进行类型转换

\verb|istream|类重载了抽取运算符\verb|>>|

格式化输入函数(formatted input functions)，因为他们可以将输入数据转换为目标指定的格式。

典型的运算符函数的原型如下\verb|istream & operator>>(int &);|

其参数和返回值，都是引用。在这里使用的是引用，意味着\verb|operator>>()|函数会处理变量本体，而不是像常规函数那样处理传入参数的副本。

\subsection{cin如何检查输入}

抽取运算符查看输入流的方法是相同的，跳过空白(空格、换行符和制表符)，直至遇到非空白字符。

读取从非空白字符开始，到与目标类型不匹配的第一个字符之间的全部内容。

\begin{lstlisting}
int elevation;
cin >> elevation;
\end{lstlisting}

对于上面这样的代码示例，如果输入-123z，那么，最终传递给\verb|elevation|的将会是-123，而Z，则可能留给下一个\verb|cin|对象来接接收

如果输入的是比如adbc，完全不符合\verb|int|类型的要求，那么不会有任何值传递给\verb|elevation|

\subsection{流状态}
cin和cout对象包含一个描述流状态(stream state)的数据成员。

流状态为\verb|iostate|类型，从\verb|ios_base|继承而来，本质是一种\verb|bitmask|类型。

包含3个\verb|ios_base|元素，\verb|eofbit|，\verb|badbit|和\verb|failbit|

每个元素都是1个bit位

当cin操作达到文件末尾时，\verb|eofbit|置为1

cin未能读取到预期字符时，\verb|failbit|置为1

发生流故障时，\verb|badbit|置为1

三个bit都为0时，说明流工作正常。

\section{流的控制与操作}
\paragraph{std::ios\_base::beg}
\verb|std::ios_base::beg| 是 C++ 标准库中 \verb|std::ios_base| 类的一个静态成员常量，用于指定流中的位置指针的起始位置，是从文件的开头开始计算偏移量。它通常与 \verb|seekg()| 和 \verb|seekp()| 函数一起使用，这两个函数分别用于设置输入流和输出流的位置指针。

可以看如下的代码示例，其中\verb|offset|是一个整数，表示从起始位置开始的偏移量(以字节为单位)
\begin{lstlisting}
stream.seekg(offset, std::ios_base::beg);
stream.seekp(offset, std::ios_base::beg);
\end{lstlisting}

\part{C++新版本特色}
\chapter{C++11新特色}
C++ Primer Plus (6th edition)原书第795页，第18章

\section{类型、声明与初始化}
\subsection{成员初始化列表}
成员初始化列表的声明和定义方式，详见第\ref{成员初始化列表}节第\pageref{成员初始化列表}页

\subsection{列表初始化}
C++11对\textbf{列表初始化}进行了扩展，使其可以用于所有内置的类型和用户定义的类型(即类对象)

使用列表初始化时，可以加上等号\verb|=|，也可以不加。

列表初始化也可以用于\verb|new|表达式中。

\verb|int * ar = new int [4] {2,4,6,7};|

然而，如果类有模板\verb|std::initializer_list|作为参数的构造函数，则，只有该构造函数可以使用列表初始化形式。

\subsubsection{优先级问题}
STL容器在执行\verb|vector(5, 5)|和\verb|vector{5, 5}|的时候，会调用不同的构造函数。第一个，会生成5个元素5的容器。第二个，会将\verb|{5, 5}|转换为\verb|initialize_list|，然后调用形参为\verb|initialize_list|的构造函数。

\subsubsection{缩窄}

列表初始化可以防止缩窄，即，禁止将数值赋值给无法存储它的数值变量。

\begin{lstlisting}
char c1 = 1.57e2;   // 将double转换为char，可能发生未定义行为
char c2 = 459585821;  // 将int转换为char，可能发生未定义行为
\end{lstlisting}

如果使用，初始化列表，上述的形式则被禁止

\begin{lstlisting}
char c1 = {1.57e2};
char c2 = {459585821};
\end{lstlisting}

但是列表初始化，仍会允许向"较宽"的类型进行转换。

另外，只要值在较窄类型的取值范围内，这样不会发生缩窄，所以也是允许的。

\subsection{std::initializer\_list}\label{initializer_list}
C++ Primer Plus (6th edition)原书第724页，第16.7.2节

\verb|initializer_list|对象中的元素永远是常量值，我们无法改变\verb|initializer_list|对象中元素的值。并且，拷贝或赋值一个\verb|initializer_list|对象不会拷贝列表中的元素，其实只是引用而已，原始列表和副本共享元素。

和使用\verb|vector|一样，我们也可以使用迭代器访问\verb|initializer_list|里的元素

如果想向\verb|initializer_list|形参中传递一个值的序列，则必须把序列放在一对花括号内
\begin{lstlisting}
void error_msg(initializer_list<string> il)
{
    for(auto beg=il.begin();beg!=il.end();++beg)
    cout<<*beg<<" ";
    cout<<endl;
}

//expected和actual是string对象
if(expected != actual)
    error_msg({"functionX",expectde,actual});
else
    error_msg({"functionX","okay"});
\end{lstlisting}

\subsubsection{列表初始化的原理}
前文说过，C++有列表初始化的功能，例如下面的代码示例
\begin{lstlisting}
std::vector<double> payments = {45.99, 39.23, 19.95, 89.01};
\end{lstlisting}

将创建一个包含4个元素的容器，并使用列表中的4个值来初始化这些元素。其原理是，容器类现在包含\verb|initializer_list<T>|作为参数的构造函数。例如\verb|vector<double>|包含一个将\verb|initializer_list<double>|作为参数的构造函数。

上述的声明与下面的代码等价。显式地将给定的列表作为构造函数的参数。
\begin{lstlisting}
std::vector<double> payments({45.99, 39.23, 19.95, 89.01});
\end{lstlisting}

如果某个类有接受\verb|initializer_list|作为参数的构造参数，则使用语法\verb|{}|将调用该构造函数。

\begin{lstlisting}
std::vector<int> vi{10};

// 等价的语句是
std::vector<int> vi({10});
// 而不是
std::vector<int> vi(10);
\end{lstlisting}

\subsubsection{对类型转换的特殊限制}
所有途径\verb|initializer_list|的参数，必须是相同的类型。但是编译器会进行必要的类型转换。但是不能进行隐式的窄化转换。

\subsection{关键字decltype}\label{关键字decltype}
关键字\verb|decltype|将变量的类型声明为表达式所制定的类型。

例如，下面的语句的含义是，让y的类型与x相同(或者说，声明一个于x的类型相同的变量y)。其中x是一个表达式。

\begin{lstlisting}
decltype(x) y;
\end{lstlisting}

\subsubsection{decltype的推导规则}
\verb|decltype|的推导规则，要注意与\verb|auto|的推导规则相区分。

\verb|decltype|的工作原理比\verb|auto|复杂。

\begin{enumerate}
\item 如果x是一个没有括号括起来的标识符，则y的类型与x相同，包括\verb|const|等限定符。如果传递给\verb|decltype|的是未加括号的成员变量，属于类的cv限定符会被忽略。
\item 如果x是一个函数调用，则y与函数返回的类型相同。但是实际上，并不会发生调用该函数的过程，只是查询了函数原型
\item 如果x是一个左值，则y为指向其类型的引用。括号并不会改变表达式的值，及其左值性。
\item 如果都不属于前面的特殊情况，则x与y的类型相同
\end{enumerate}

\begin{lstlisting}
int j = 3;
int &k = j;
const int &n = j;
decltype(n) i1;  // i1的类型是const int &
decltype(j) i2;  // i2的类型是int
decltype((j)) i3;  // i3的类型是int &
decltype(k + 1) i4;  // i4的类型是int
\end{lstlisting}

\subsubsection{typeid}
详见\ref{typeinfo}

\subsubsection{(C++14)与auto连用}
\verb|decltype|也可以用来推导\verb|auto|，但是依据的是\verb|decltype|的推导规则。

\verb|decltype(auto)|也可以连起来用作非模板形参占位符

\begin{lstlisting}
template<decltype(auto) N>
\end{lstlisting}

\subsubsection{(C++20)非类型模板参数}
如果要推导的是一个非类型模板参数，那么 \verb|decltype| 生成该模板形参的类型。


\subsection{说明符constexpr}
\verb|constexpr|在C++11开始加入标准。

\verb|constexpr|说明符，声明可以在编译时对函数或变量求值。

\paragraph{不同适用场景}
\verb|constexpr|说明符用于对象，则包含了\verb|const|的功能。\verb|constexpr|说明符用于函数，则包含了\verb|inline|的功能。

\paragraph{传播性}
如果函数或函数模板的一个声明拥有 \verb|constexpr| 说明符，那么它的所有声明都必须含有该说明符。

\subsubsection{构造函数使用constexpr}
保证传递给该构造函数的所有参数都是\verb|constexpr|，那么产生的对象的所有成员都是\verb|constexpr|。

该对象是\verb|constexpr|对象了，可用于只使用\verb|constexpr|的场合。 

\subsubsection{与const的区别}
\paragraph{初始化的时机不同}
\verb|const|变量的初始化可以延迟到运行时，而\verb|constexpr|变量必须在编译期完成求值。

所有 \verb|constexpr| 变量均为常量，因此必须使用常量表达式初始化。

\paragraph{应用于指针的情况}
与\verb|const|不同，在\verb|constexpr|声明中如果定义了一个指针，限定符\verb|constexpr|仅对指针有效，与指针所指对象无关。

\paragraph{在编译器优化方面}
\verb|constexpr|是一种很强的约束，更好的保证程序的正确定语义不被破坏；编译器可以对\verb|constexper|代码进行非常大的优化，例如：将用到的\verb|constexpr|表达式直接替换成结果, 相比宏来说没有额外的开销。

\subsubsection{(C++20)新增特性}
\begin{enumerate}
    \item 支持虚函数是\verb|constexpr|
    \item 支持在上下文中使用动态分配
    \item 支持在\verb|constexpr|函数中使用\verb|try-catch|块
    \item 增加了\verb|constexpr|构造函数的功能。
\end{enumerate}

\subsection{返回类型后置}
C++11新增了一种函数声明语法，在函数名和参数列表的后面(而非前面)指定函数的返回类型。

\begin{lstlisting}
double f1(double, int);  // 传统写法
auto f2(double, int) -> double;  // 新的写法，返回值是double类型
\end{lstlisting}

这种写法，与关键字\verb|decltype|相结合，在声明模板函数时可以起到特别的作用。

\begin{lstlisting}
template<typename T, typename U>
auto eff(T t, U u) -> decltype(T+U)
{
    ...
}
\end{lstlisting}

在编译器遇到\verb|eff|的参数列表之前，T和U还不在作用域内。因此，必须在参数列表后使用\verb|decltype|

\subsubsection{(C++14)与auto连用}
C++14之后，支持\verb|auto|和\verb|decltype|联合起来使用，进行返回类型后置。
\begin{lstlisting}
template<class T1, class T2>
auto sum1(T1 t1, T2 t2) -> decltype(t1 + t2){return t1 + t2;}
\end{lstlisting}

注意，之所以提出这种连用方法，是因为下面的写法是不合法的。因为编译器读到\verb|t1 + t2|的时候，还不知道\verb|t1|和\verb|t2|是什么。

\begin{lstlisting}
decltype(t1 + t2) sum1(T1 t1, T2 t2) {return t1 + t2;}
\end{lstlisting}


\subsection{类型别名(type alias)}
对于冗长或复杂的标识符，C++提供了\verb|typedef|来为其起一个简洁的别名。同时，也可以使用\verb|using|。

\verb|using|的作用域，是\verb|using|语句当前所处的作用域。

\begin{lstlisting}
typedef std::vector<std::string>::iterator itType;
using itType = std::vector<std::string>::iterator;
\end{lstlisting}

区别在于，\verb|using|可以用于模板具体化，而\verb|typedef|不行。

\begin{lstlisting}
template<typename T>
    using arr12 = std::array<T,12>;
...
// std::array<double, 12> a1;可以替换为
arr12<double> a1;
// std::arrat<std::string, 12> a2;可以替换为
arr12<std::string> a2;
\end{lstlisting}

\subsubsection{模板别名}
C++11支持给模板也起一个别名，详见第\ref{模板别名}节

\subsection{nullptr}

空指针，是不会指向有效数据的指针。

以前的C++使用0表示这种指针，但是底层表示可能不同，这可能会带来一些问题(0既可以表示指针常量，又可以表示整型常量)。

\verb|nullptr|是指针类型，无法被转换为整型类型。

为了向后兼容，C++11仍允许使用0表示空指针。所以，\verb|nullptr == 0|的结果是\verb|true|

\section{非受限联合类型(union)}
传统的\verb|union|类型的成员类型不能是一个非平凡类型，必须是POD类型，为了保持与C的兼容。C++11取消了这种限制。

C++11 标准规定，任何非引用类型都可以成为联合体的数据成员，这种联合体也被称为非受限联合体。

C++11 允许联合体有静态成员
\begin{lstlisting}
union U {
    static int func() {
        int n = 3;
        return n;
    }
}; // 需要注意的是，静态成员变量只能在联合体内定义，却不能在联合体外使用，这使得该规则很没用。
\end{lstlisting}

如果非受限联合体内有一个非 POD 的成员，而该成员拥有自定义的构造函数，那么这个非受限联合体的 默认构造函数将被编译器删除。

\begin{lstlisting}
#include <string>
using namespace std;
union U {
    string s; //string类有自定义的构造函数，所以U的构造函数会被删除。  则 U u; 无法定义成功
    int n;
};
int main() {
    U u;   // 构造失败，因为 U 的构造函数被删除
    return 0;
}
\end{lstlisting}

解决方案：使用placement new显式调用string类的构造函数。
\begin{lstlisting}
#include <string>
using namespace std;
union U {
    string s;
    int n;
public:
    U() { new(&s) string; }  //采用 placement new 将 s 构造在其地址 &s 上
    ~U() { s.~string(); } //在析构时还需要调用 string 类的析构函数
};
int main() {
    U u;
    return 0;
}
\end{lstlisting}

\subsubsection{匿名联合体}
下面的代码示例，举例了匿名联合体的样子。
\begin{lstlisting}
union U{
    union{int x; } //此联合体为匿名联合体
}
\end{lstlisting}

当非受限的匿名联合体运用于类的声明时，这样的类被称为“枚举式类”。

匿名非受限联合体作为类 Singer 的“变长成员”来使用，这样的变长成员给类的编写带来了更大的灵活性。
\begin{lstlisting}
#include<cstring>
using namespace std;

class Student{
    public:
    Student(bool g, int a): gender(g), age(a){}
    bool gender;
    int age;
};
class Singer { // 枚举式类
public:
    enum Type { STUDENT, NATIVE, FOREIGENR };
    Singer(bool g, int a) : s(g, a) { t = STUDENT; }
    Singer(int i) : id(i) { t = NATIVE; }
    Singer(const char* n, int s) {
        int size = (s > 9) ? 9 : s;
        memcpy(name , n, size);
        name[s] = '\0';
        t = FOREIGENR;
    }
    ~Singer(){}
private:
    Type t;
    union { //匿名联合体
        Student s;
        int id;
        char name[10];
    };
};
\end{lstlisting}


\section{基于范围的for循环}\label{基于范围的for循环}
C++ Primer Plus (6th edition)原书第684页，第16.3.4节

基于范围的for循环指的是，在\verb|for|语句中，先声明一个类型与待遍历的数据结构里的元素的类型相同的变量，然后在\verb|:|之后跟着待遍历的数据结构的变量名。

\subsubsection{基本格式}
\begin{lstlisting}
for(declaration : expression)
    statement
\end{lstlisting}

在下面的代码示例中，x表示\verb|prices[5]|里面的每一个元素，然后执行循环体。

\begin{lstlisting}
double prices[5] = {4.99, 10.99, 6.87, 7.99, 8.49};
for (double x: prices)
    cout << x << std::endl;
\end{lstlisting}

\subsubsection{修改原数据的值}
在下面的代码示例中，使用了引用(关于引用的概念请参考"引用"章节)，可以修改原数组的值，而上面的用法不会修改。

\begin{lstlisting}
double prices[5] = {4.99, 10.99, 6.87, 7.99, 8.49};
for (double &x: prices)
    cout << x << std::endl;
\end{lstlisting}

也可以使用\verb|auto|

\begin{lstlisting}
std::vector<int> vi(6);
for (auto & x: vi)
    x = std::rand();
\end{lstlisting}

\verb|auto|与引用连用，修改容器内成员的值

\begin{lstlisting}
void InflateReview(Review & r) {...}
...
for (auto & x : books) InflateReview(x);
\end{lstlisting}

\subsubsection{适用于自定义的类}
如何实现一个支持基于范围的for循环的类？

该类型必须有一组与其本身的类型相关的\verb|begin|和\verb|end|函数，可以是类型的成员函数也可以是独立函数。\verb|begin|和\verb|end|函数需要返回一组类似于迭代器的对象，并且这组对象必须支持\verb|operator*|，\verb|operator!=|，\verb|operator++|

\subsubsection{(C++17)更改了底层定义}
C++11中，将基于for循环的整个结构，替换成如下代码
\begin{lstlisting}
std::vector<int> v = {1, 2, 3, 4};
{
    auto && __range = v ; 
    for (auto __begin = v.begin(), __end = v.end(); 
    __begin != __end; ++__begin) { 
        int i = *__begin; 
        std::cout << i << " ";
    }
}
\end{lstlisting}

而C++17中，更改了实现。

\begin{lstlisting}
{        
    auto && __range = range_expression ; 
    auto __begin = begin_expr ;
    auto __end = end_expr ;
    for ( ; __begin != __end; ++__begin) { 
        range_declaration = *__begin; 
        loop_statement 
    } 
}
\end{lstlisting}

C++17，begin和end不必返回相同的类型。

\section{关键字auto - 类型占位符}
C++11利用关键字\verb|auto|进行自动类型推断。关键字\verb|auto|当做类型占位符，自动推断变量的类型，或者函数返回类型后置时，在函数返回类型处作占位符。

C++11以前，\verb|auto|关键字有另外的含义，但是C++11把旧意义删去了，赋予了新含义。

\begin{lstlisting}
auto maton = 112;  // maton是int类型
auto pt = &maton;  // pt是int*类型
double fm(double, int);
auto pf = fm;  // pf是double*类型
\end{lstlisting}

类型推导是发生在编译期的(这句话的正确性目前看来仅限于C++11，在之后的版本可能不适用了)，在编译期如果无法推导的话，会报错。

\begin{lstlisting}
auto i;  // 编译失败，编译期无法决策i的类型
\end{lstlisting}

\subsection{常见用法}
\verb|auto|的使用场景，可简单总结为以下几条。

\begin{enumerate}
    \item 明确知晓初始化类型时可使用auto
    \item 复杂的类型，例如：lambda表达式，\verb|bind|以及STL迭代器。lambda和\verb|bind|类型有时候我们自己都很难描述，而STL迭代器则是因为类型太长而影响代码阅读。
\end{enumerate}

可知，非常简单的类型推断，可以使用\verb|auto|，而非常复杂的类型名，在一些显而易见的场合下，也可以用\verb|auto|。

\begin{lstlisting}
std::unordered_multimap<std::string, int> unorderedMap;

unorderedMap.insert(std::make_pair("li", 10));
unorderedMap.insert(std::make_pair("liu", 50));
unorderedMap.insert(std::make_pair("wu", 49));

std::unordered_multimap<std::string, int>::iterator iter = unorderedMap.find("li"); 
//  auto iter = unorderedMap.find("li");  使用auto iter定义可简化为auto iter;
if (iter != unorderedMap.end()) //查找成功
{
    std::cout << "name: " << iter->first << "age: " << iter->second;
}

// 你可以准确的描述fnIsEven的具体类型吗? 在此只有auto最简单。
auto fnIsEven = [](int value) -> bool {
    return (0 == value % 2);
};
\end{lstlisting}

\subsubsection{用于STL迭代器}
关键字\verb|auto|还可以简化声明。

下面的代码展示了如何进行简化。

\begin{lstlisting}
for (std::initializer_list<double>::iterator p = il.begin(); 
p != il.end(); p++)
-->
for (auto p = il.begin(); p != il.end(); p++)
\end{lstlisting}

\subsection{(C++11)类型推导规则}
\subsubsection{auto与多变量，左结合性，从左往右}
编译器对类型的推导，是从左往右的。

\begin{lstlisting}
int value = 8;
auto *pValue = &value, n = 10;  // auto 推导为int, n也为int,可以编译通过

auto *pValue = &value, m = 10.0; // auto 推导为int, m也为int，m不能赋值10.0
\end{lstlisting}

\subsubsection{按值初始化忽略cv限定符}
在\verb|auto|声明变量时，如果没有使用引用也没有使用指针。那么编译期推导时会忽略cv限定符。但是如果\verb|auto|声明时使用了指针或引用，那么cv限定符不会忽略

\begin{lstlisting}
const int i = 12;
auto j = i;   // j会被推导为int
auto &k = i;  // k声明时使用&，auto会被推导为const int,k类型为const int&
auto *x = &i; // x声明使用指针* auto会被推导为const int, x类型为const int*
const auto y = i; // auto 推导为int， y类型为const int
\end{lstlisting}

\subsubsection{按引用初始化忽略引用属性}
如果使用一个引用对象去初始化\verb|auto|类型对象，那么原对象的引用属性会被忽略

\begin{lstlisting}
const int i = 12;
auto &k = i;  // k声明时使用&，auto会被推导为const int,k类型为const int&
auto j = k;   // j 会推导为int 因为此处k的引用属性将会被忽略
\end{lstlisting}

\subsubsection{auto与\&\&}
\verb|auto|与\verb|&&|结合时，\verb|&&|是万能引用而非右值引用

如果初始化表达式为左值，根据引用折叠原理\verb|auto|会推导为引用。

如果初始化表达式为右值，\verb|auto|会推导为表达式字面值类型。

\begin{lstlisting}
int i = 100;
auto&& j = i;     // 引用折叠，auto&& 推导为int&, 所以j的类型为int&
auto&& k = 200;   // 引用折叠，auto&& 推导为int&&, 所以k的类型为int&&
\end{lstlisting}

\subsubsection{auto与数组、函数}
\verb|auto|与数组结合，数组会退化为数组指针。

\verb|auto|与函数结合，函数也会退化为函数指针。

这本身是由数组名和函数名的自身特性决定的，并不是\verb|auto|的特性。单独存在的数组名，就是指向数组第一个元素位置的指针。

\begin{lstlisting}
int array[100] = {0};
auto i = array;   // i推导为int*

auto min(int a, int b) -> int 
{
    return (a < b) ? (a): (b);
}

auto fn = min;   // fn 推导的类型为int (*)(int, int)
\end{lstlisting}

\subsubsection{auto与三目运算符}
\verb|auto|与三目运算符结合进行类型推导，编译期总是使用数据范围更大的类型

\begin{lstlisting}
auto min = (true) ? 100, 2.3;  // min会被推导为double,因为double比int类型范围更大。
\end{lstlisting}

\subsubsection{auto与成员声明初始化}
C++11规定\verb|auto|无法应用于类的非静态成员初始化。仅可以应用于\verb|const static|的成员变量。

\begin{lstlisting}
struct Person
{
    auto age = 15;  // 编译失败，不允许这么声明
};

struct Person
{
    static const auto age = 15; // C++11可以编译通过 
};

\end{lstlisting}

C++17开始，放宽了这个限制，见第\ref{C++17之后}节。

\subsubsection{auto与列表初始化}
C++11标准下，直接列表初始化和等号赋值列表初始化，均推导为\verb|std::initializer_list|

\begin{lstlisting}
auto d = {1, 2};  // d的类型是 std::initializer_list<int>
auto n = {5};     // n 的类型是 std::initializer_list<int>
auto e{1, 2};     // C++11标准，e的类型是std::initializer_list<int>
auto m{5};        // C++11标准，m的类型是std::initializer_list<int>
\end{lstlisting}

C++17开始，更改了\verb|auto|与列表初始化的推导规则，见第\ref{C++17之后}节。







\subsection{C++14之后}
\subsubsection{(C++14)decltype(auto)}
C++14开始可以将\verb|decltype|与\verb|auto|连用，允许程序员以一个简单的形式声明与某某变量类型相同的变量，而无须严格地复制粘贴完整的类型名。

\begin{lstlisting}
auto x = 1 + 2;
decltype(auto) y = x;           // y的类型是int，持有x的副本          
decltype(auto) z = (x);         // z的类型int&, z是x的引用  
\end{lstlisting}

C++20开始，对于\verb|decltype(auto)|语句新增了\textbf{类型约束功能}

\subsubsection{(C++14)允许返回类型声明为auto}
支持函数或lambda表达式的返回类型声明为\verb|auto|，允许\verb|return|语句的操作数推导返回\verb|auto|类型

下面的代码示例展示了，无论是模板函数，还是lambda表达式，都可以将其返回类型声明为\verb|auto|。

\begin{lstlisting}
template<class T, class U>
auto add(T t, U u)              // 返回类型是 operator+(T, U) 的类型
{ 
    return t + u; 
}

auto lambda = [](int x) { return x + 3; };
\end{lstlisting}

\subsubsection{(C++14)可用于lambda表达式}
C++14开始，lambda表达式的形参，也可以使用\verb|auto|了。

\begin{lstlisting}
auto lambda1 = [](auto a, auto b) {return a + b;}; // lambda1的返回类型为decltype(a+b)
auto value = lambda1(1, 3.0);   // value 被推导为double
\end{lstlisting}

\subsection{C++17之后}\label{C++17之后}
\subsubsection{(C++17)可用于非类型模板形参的占位符}
C++17，auto也可以作为非类型模板形参的占位符。

从C++17标准开始，auto开始支持非类型模板的声明，auto非类型模板同样需要遵守非类型模板标准，即推导出的类型必须是可作为模板实参的。

除非类型模板声明，C++17标准还允许auto 说明符应用于结构化绑定声明。

\subsubsection{(C++17)可用于类的非静态成员初始化}\label{(C++17)可用于类的非静态成员初始化}
\begin{lstlisting}
struct Person
{
    static auto age = 15; // C++17可以编译通过 
};
\end{lstlisting}

\subsubsection{(C++17)更改了auto与列表初始化的推导规则}
C++17标准下，等号赋值列表初始化推导为\verb|std::initializer_list|，直接赋值初始化仅允许单个元素

\begin{lstlisting}
auto d = {1, 2};  // d的类型是 std::initializer_list<int>
auto n = {5};     // n 的类型是 std::initializer_list<int>
auto e{1, 2};     // C++17标准，编译报错误
auto m{5};        // C++11标准，m的类型是int
\end{lstlisting}

\subsection{C++20之后}
\subsubsection{(C++20)可用于函数形参}
C++20之前，auto不能用于函数形参。

\begin{lstlisting}
void f1(auto); // 与 template<class T> void f(T) 相同

auto min(auto a, auto b)
{
    return (a < b) ? (a) : (b);
}

auto val1 = min(2, 5.0);  // val1推导为double
auto val2 = min(2.0, 5.0);// val2推导为double
auto val3 = min(2.0, 5);  // val3推导为double
\end{lstlisting}

\section{作用域内枚举}

详见第\pageref{作用域内枚举(C++11)}页第\ref{作用域内枚举(C++11)}节

\section{智能指针}
\subsection{问题背景}
\subsubsection{没有智能指针时，会遇到那些困难}
内存资源释放了，但是指针没有指向\verb|nullptr|。会导致以下几种情况

\begin{enumerate}
    \item 野指针，访问一个已销毁或者访问受限的内存区域的指针。
    \item 指针悬挂，出现在多个指针指向同一个资源的时候。通过其中一个指针释放了资源，且也指向了\verb|nullptr|，但是其他指针并不知道资源已被释放。
    \item 踩中其他内存，内存释放后被其他资源占用了，导致读取到了其他资源的内存。
    \item 内存泄露，通过\verb|new|在堆区域分配了内存并用指针来使用它，已停止使用该指针但是并没有释放堆区域的内存，导致这一块堆内存无法再被使用，除非进程停止，进程的地址空间整个都被回收。
\end{enumerate}

\subsubsection{内存泄露的情况}
没有资源释放，但是产生了内存泄漏。

先看下面的代码示例。

\begin{lstlisting}
void remodel(std::string & str)
{
    std::string * ps = new std::string(str);
    ...
    str = ps;
    return;
}
\end{lstlisting}

其中的缺陷是，每次调用时，该函数都需要分配堆里的内存(因为用到了\verb|new|)，但是并没有对应地回收该内存，从而导致内存泄露。

虽然，可以警告程序员，让他们在编程的时候牢记需要释放分配了的内存\verb|delete ps;|，但是这是不恰当的。

而且当该函数出现异常时，后面的\verb|delete|语句也不会被运行。

问题在于，\verb|ps|只是一个常规指针，不是带有析构函数的一个类对象。如果他是类对象，则他在过期时会自动调用析构函数，删除指向的内存。

\subsubsection{重复释放的情况}
重复释放资源，容易引发内存core dump。

先看下面的代码示例

\begin{lstlisting}
auto_ptr<string> ps (new string("I reigned lonely as a cloud."));
auto_ptr<string> vocation;
vocation = ps;
\end{lstlisting}

如果\verb|vocation|和\verb|ps|是常规指针，则两个指针将指向同一个string对象。

但是这会出现一个问题，程序将删除同一个对象两次，一次是\verb|vocation|过期的时候，一次是\verb|ps|过期的时候。

\subsubsection{提出智能指针}

为了优化\verb|new|和\verb|delete|必须成对匹配使用时容易发生遗漏等种种与指针实际使用有关的问题，C++引入了智能指针。

\textbf{智能指针}指的是行为类似于指针的类对象。

C++11抛弃了\verb|auto_ptr|，而新增了\verb|unique_ptr|、\verb|shared_ptr|、\verb|weak_ptr|

本节介绍三个可以帮助管理动态内存分配的智能指针模板。

\verb|auto_ptr|在C++98中提出，但是在C++11中已经被摒弃。C++11中又新提出了后面两种方案。

\subsection{使用智能指针}
C++ Primer Plus (6th edition)原书第668页，第16.2.1节

创建智能指针对象，必须包含头文件\verb|memery|

三个智能指针模板，都定义了类似指针的对象，都可以将\verb|new|获得的地址赋值给这些对象。

当智能指针过期时，其析构函数将使用\verb|delete|来释放内存。

\begin{lstlisting}
auto_ptr<double> pd(new double);
auto_ptr<std::string> ps(new std::string);
\end{lstlisting}

\verb|new double|是\verb|new|返回的指针，指向新分配的内存块。它是构造函数\verb|auto_ptr<double>|的参数。

其他两种智能指针使用同样的语法。

\subsubsection{通过make\_shared创建智能指针}
\verb|make_shared|的语法

\begin{lstlisting}
std::shared_ptr<T> make_shared< T >( Args&&... args );
\end{lstlisting}

常见的使用方式

\begin{lstlisting}
std::shared_ptr<MyClass> ptr = std::make_shared<MyClass>();
\end{lstlisting}


使用\verb|auto_ptr|修改上一节的代码，修改后如下

\begin{lstlisting}
#include <memory>
void remodel(std::string & str)
{
    std::auto_ptr<std::string> ps (new std::string);
    ...
    if ( weird_thing() )
    throw exception();
    str = *ps;
    return;
}
\end{lstlisting}

所有智能指针都有一个\verb|explicit|构造函数

\subsection{删除器}
\subsubsection{为智能指针配置删除器}
删除器(deletor)，指的是一个可调用对象(lambda表达式，重载了\verb|operator()|的类或者结构体，函数对象，等等)，智能指针在结束生命周期时，先调用这个指定的删除器，再调用析构函数。
\begin{lstlisting}
template< class Y, class Deleter >
shared_ptr( Y* ptr, Deleter d );
\end{lstlisting}


\begin{lstlisting}
struct Foo {
    int id{0};
    Foo(int i = 0) : id{i} { std::cout << "Foo::Foo(" << i <<  ")\n"; }
    ~Foo() { std::cout << "Foo::~Foo(), id=" << id << '\n'; }
};

struct D {
    void operator()(Foo* p) const {
        std::cout << "Call delete from function object. Foo::id=" << p->id << '\n';
        delete p;
    }
};    
    
int main() {
    std::cout << "3) constructor with object and deleter\n";
    std::shared_ptr<Foo> sh4(new Foo{11}, D());
    std::shared_ptr<Foo> sh5(new Foo{12}, [](auto p) {
        std::cout << "Call delete from lambda... p->id=" << p->id << '\n';
        delete p;
    });
}
\end{lstlisting}

程序输出为。
\begin{lstlisting}
3) constructor with object and deleter
Foo::Foo(11)
Foo::Foo(12)
Call delete from lambda... p->id=12
Foo::~Foo(), id=12
Call delete from function object. Foo::id=11
Foo::~Foo(), id=11
\end{lstlisting}

可见，智能指针先后创建了2个\verb|Foo|对象，分别打印了\verb|Foo::Foo(11)|和\verb|Foo::Foo(12)|。

然后由于作用域结束了，所以智能指针开始析构(析构的顺序是，创建顺序的倒序)。

先析构\verb|Foo{12}|，调用他的删除器，他的删除器是由lambda表达式给出的，然后调用其析构函数\verb|Foo::~Foo(), id=12。|

然后析构\verb|Foo{11}|，调用他的删除器，他的删除器是由重载了\verb|operator()|的\verb|struct|给出的。

\subsubsection{shared\_ptr的删除器}
对于\verb|shared_ptr|, 不管删除器什么类型，是否有状态都不会增加\verb|shared_ptr|的大小, 均为两个字长。因为删除器是存储在控制块中，而控制块的大小为两个字长。

\subsubsection{unique\_ptr的删除器}
\verb|unique_ptr|的删除器类型是一个模板参数，因此需要指定删除器类型。

如果删除器是函数指针类型，\verb|unique_ptr|大小从1个字长增长到2个字长，因为需要存储函数指针。

如果删除器是无状态删除器(stateless function)，比如不进行捕获的lambda表达式，\verb|unique_ptr|大小不变，因为无状态删除器不需要存储任何成员变量。

\begin{lstlisting}
std::unique_ptr<int, FreeMemory> up1(new int(0));  // size: 4
std::unique_ptr<int, void(*)(int*)> up2(new int(0), free_memory);  // size: 8
std::unique_ptr<int, decltype(free_memory)*> up3(new int(0), free_memory);  // size: 4
\end{lstlisting}

\subsubsection{有状态删除器和无状态删除器}
有状态删除器是指删除器类中包含有成员变量，无状态删除器是指删除器类中不包含有成员变量。

如果\verb|unique_ptr|的函数对象删除器是具有扩展状态的，其大小可能会非常大。如果大得无法接受，可能需要设计一个无状态删除器。

下面举例一个有状态删除器
\begin{lstlisting}
class DeleteObject {
    public:
        DeleteObject(int n) : n_(n) {}
        void operator()(int* p) {
            std::cout << "delete memory " << n_ << std::endl;
            delete p;
        }
    private:
        int n_;
};
\end{lstlisting}

\subsection{std::enable\_shared\_from\_this}
\verb|std::enable_shared_from_this|是一个模板类，定义于头文件\verb|<memory>|

对于一个已经被\verb|shared_ptr|管理起来的资源，\verb|std::enable_shared_from_this|可以生成一个智能指针，与之前的智能指针共享地管理这个资源。

假如一个类T继承了\verb|std::enable_shared_from_this<T>|，则会为该类T提供一个成员函数\verb|shared_from_this|。

当 \verb|T| 类型对象 \verb|t| 被一个为名为 \verb|pt| 的 \verb|std::shared_ptr<T>| 类对象管理时，调用 \verb|T::shared_from_this| 成员函数，将会返回一个新的 \verb|std::shared_ptr<T>| 对象，它与 \verb|pt| 共享 \verb|t| 的所有权。

\subsubsection{使用场合}
当类被\verb|shared_ptr|管理，且在类 \verb|A| 的成员函数里需要把当前类对象作为参数传给其他函数时，就需要传递一个指向自身的 \verb|shared_ptr| 。

\paragraph{为何不直接传递this指针}
使用智能指针的初衷就是为了方便资源管理，如果在某些地方使用智能指针，某些地方使用原始指针，很容易破坏智能指针的语义，从而产生各种错误。

\paragraph{可以直接传递shared\_ptr<this>么}
不可以。

在拷贝的时候， \verb|shared_ptr| 他的共享信息，也需要妥善地传递。如果没有处理好，就会发生多个 \verb|shared_ptr| 指向同一个对象，但是他们之间并不知道对方存在的情况。此时引用计数就失效了。一旦其中一个引用计数器归零，就会提前释放资源，造成另外一个 \verb|shared_ptr| 悬空。
\begin{lstlisting}
#include <memory>
#include <iostream>
 
class Bad
{
public:
	std::shared_ptr<Bad> getptr() {
		return std::shared_ptr<Bad>(this);
	}
	~Bad() { std::cout << "Bad::~Bad() called" << std::endl; }
};
 
int main()
{
	// 错误的示例，每个shared_ptr都认为自己是对象仅有的所有者
	std::shared_ptr<Bad> bp1(new Bad());
	std::shared_ptr<Bad> bp2 = bp1->getptr();
	// 打印bp1和bp2的引用计数
	std::cout << "bp1.use_count() = " << bp1.use_count() << std::endl;
	std::cout << "bp2.use_count() = " << bp2.use_count() << std::endl;
}  // Bad 对象将会被删除两次
\end{lstlisting}

\subsection{智能指针的原理}
C++ Primer Plus (6th edition)原书第670页，第16.2.2节

\subsubsection{原理}

\begin{itemize}
\item \textbf{深拷贝}  定义赋值运算符，执行深拷贝。也就是，复制指针的时候，也要复制原数据一份，并令新复制的指针指向新的值(新的一块内存)。
\item 建立\textbf{所有权(ownership)}的概念，对于特定的对象只能有一个智能指针可以拥有它。只有拥有对象的智能指针的析构函数会删除该对象。赋值操作能够转让所有权。这就是\verb|auto_ptr|和\verb|unique_ptr|采用的策略。
\item \textbf{引用计数(reference counting)}技术，创建智能程度更高的指针，跟踪引用特定对象的智能指针数。赋值时，计数+1，而指针过期时，计数将-1。仅当最后一个指针过期时，才调用\verb|delete|。\verb|shared_ptr|采用这种策略。使用\verb|use_count()|可以返回引用计数
\end{itemize}

\subsubsection{shared\_ptr的缺陷}
\verb|shared_ptr|的缺陷，”循环引用”现象。解决”循环引用”，将成对的\verb|shared_ptr|其中一个改为弱指针\verb|weak_ptr|，子对象并不实际上拥有父对象，不影响父对象的引用计数。

\verb|shared_ptr|需要维护一个原子化的引用计数器，效率低，而且需要一块额外的内存。访问对象，实际上需要二级指针来实现。

\subsubsection{使用weak\_ptr解决循环引用问题}
类B内部只是想要引用一下A中的对象，B中根本无需管理它的生命周期，它不在B中创建，也不在B中销毁。这个时候我们只用将B中的对A的引用使用\verb|weak_ptr|来引用，这样会更加合适，也避免了循环引用。

\begin{lstlisting}
class A {
    public:
    shared_ptr<B> b;
};
class B {
    public:
    weak_ptr<A> a;
};
int main(int argc, const char * argv[]) {
    shared_ptr<A> spa = make_shared<A>();//资源A引用计数为1
    shared_ptr<B> spb = make_shared<B>();//资源B引用计数为1
    spa->b = spb; //B引用计数为2，
    spb->a = spa; //A引用计数为1，
    return 0; 
} //main函数退出后，A先释放，B再释放，循环解开了
\end{lstlisting}

\subsubsection{unique\_ptr和auto\_ptr的区别}
\begin{lstlisting}
auto_ptr<string> p1(new string("auto"));
auto_ptr<string> p2;
p2 = p1;

unique_ptr<string> p3(new string("auto"));
unique_ptr<string> p4;
p4 = p3;
\end{lstlisting}

在执行\verb|p2 = p1|时，\verb|p1|的所有权将被\verb|p2|接管。但是程序后面如果试图继续使用\verb|p1|，则会出现不可控的问题，因为\verb|p1|不再指向有效的数据。

而对于\verb|unitque_ptr|来说，编译器会认为\verb|p4 = p3|是非法的。

因此我们常说，\verb|unitque_ptr|比\verb|auto_ptr|更安全(因为编译阶段的错误，比潜在的程序运行时期发生的错误，更安全)

\subsection{如何选择智能指针}
如果程序要使用多个指向同一个对象的指针，应选择\verb|shared_ptr|

很多STL算法都支持复制和赋值操作，这些操作可用于\verb|shared_ptr|，但是不能用于\verb|unique_ptr|(编译器会发出警告)和\verb|auto_ptr|(发生无法确定的行为)

如果你的编译器里没有提供\verb|shared_ptr|，可以用Boost库提供的\verb|shared_ptr|

如果你的编译器里没有提供\verb|unique_ptr|，可以用Boost库提供的\verb|scoped_ptr|

\subsubsection{智能指针作为类的成员时}
\verb|unique_ptr|，当该对象仅仅属于我，比如父窗口指向子窗口的指针。该对象不属于我，但他释放前我必须也被释放，比如子窗口指向父窗口的指针。

\verb|shared_ptr|，该对象由多个对象共享时，或者虽然该对象仅属于我，但有使用\verb|weak_ptr|的需要。

\verb|weark_ptr|，该对象不属于我，且他释放时我仍然可能不被释放，比如指向窗口中上一次被点击的元素。

\subsection{智能指针的线程安全性}
\verb|shared_ptr|指针类有两个成员变量，一个是指向变量的指针；一个是资源被引用的次数，引用次数加减操作内部自动加锁解锁，是线程安全的。

\subsubsection{引用计数是临界资源}
引用计数也是通过指针来管理的，堆上的一块内存存储着计数的数值，智能指针中的引用计数成员指向这块内存。

虽然引用计数存在于每一个\verb|shared_ptr|对象中，但是实际上它是要跟随对象所管理的资源。引用计数会随着指向这块资源的\verb|shared_ptr|对象的增加而增加。因此引用计数是要指向同一块资源的所有的对象共享的，所以实际上引用计数在\verb|shared_ptr|底层中是以指针的形式实现的，所有的对象通过指针访问同一块空间，从而实现共享。

那么也就是说，引用计数是一个临界资源，所以在多线程中，我们必须要保证临界资源访问的安全性，因此在\verb|shared_ptr|底层中在对引用计数进行访问之前，首先对其加锁，当访问完毕之后，在对其进行解锁。

来自cppreference的原文：To satisfy thread safety requirements, the reference counters are typically incremented using an equivalent of \verb|std::atomic::fetch_add| with \verb|std::memory_order_relaxed| (decrementing requires stronger ordering to safely destroy the control block).

所以\verb|shared_ptr|的引用计数是线程安全的。

\subsubsection{被shared\_ptr对象所管理的资源也是临界资源}
\verb|shared_ptr|对象所管理的资源存放在堆上，它可以由多个\verb|shared_ptr|所访问，所以这也是一个临界资源。因此当多个线程访问它时，会出现线程安全的问题。

\subsubsection{多线程访问同一个shared\_ptr时，应注意互斥}
多个线程访问同一个\verb|shared_ptr|时，应该要进行加锁操作。

修改指向时，"修改指向"和"修改引用计数"这两个步骤，可能因为CPU执行顺序问题，而带来data race，所以这种情况是线程不安全的。

\subsubsection{由不同的shared\_ptr管理相同一个对象}
将\verb|shared_ptr|按值传递给lambda表达式，或者按值传递给线程。

这时候每个线程内看到的\verb|shared_ptr|，他们所管理的是同一份数据，用的是同一个引用计数。但是各自是不同的对象，当发生多线程中修改\verb|shared_ptr|指向的操作的时候，是不会出现非预期的异常行为的。

但是，此时修改\verb|shared_ptr|指向的对象，仍然会带来data race现象。

\section{线性局部存储thread\_local}\label{线性局部存储thread_local}
将对象内存和线程关联在一起。

编译器也提供了这方面的支持，gcc提供了\verb|__thread|，而MSVC使用\verb|__declspec(thread)|。

C++11提供了\verb|thread_local|说明符。

线程局部存储(Thread Local Storage，TLS)是一种编程技术，用于为每个线程提供独立的变量副本。它允许在多线程程序中创建全局变量的多个实例，每个实例都与特定的线程相关联。

传统上，全局变量是所有线程共享的，在并发环境下可能导致竞态条件和数据访问冲突。而通过使用线程局部存储，可以使每个线程都拥有自己独立的变量副本，从而避免了竞态条件问题。

\subsubsection{适用场景}
线程局部存储通常用于以下情况
\begin{enumerate}
    \item 线程特定数据：某些数据只对特定线程有意义，并且需要在线程内保持状态。
    \item 全局状态隔离：将全局状态分离为每个线程的私有副本，以提高并发性能。
    \item 线程上下文保存：将当前执行线程的上下文信息保存在特定于该线程的变量中。
\end{enumerate}

\subsubsection{特性}
\begin{enumerate}
    \item 变量在线程创建时生成(不同编译器实现略有差异，但在线程内变量第一次使用前必然已构造完毕)。
    \item 线程结束时被销毁。析构，利用析构特性，\verb|thread_local|可以感知线程销毁事件。
    \item 每个线程都拥有其自己的变量副本。
    \item \verb|thread_local|可以和\verb|static|或\verb|extern|联合使用，这将会影响变量的链接属性。
\end{enumerate}

\subsubsection{示例}
在下面的代码示例中，变量\verb|a|在\verb|main|线程和\verb|t|线程中分别保留了一份副本，\verb|a|的地址不同。
\begin{lstlisting}
// thread_local.cpp
#include <iostream>
#include <thread>

class A {
public:
    A() {
        std::cout << std::this_thread::get_id()
        << " " << __FUNCTION__
        << "(" << (void *)this << ")"
        << std::endl;
    }
    
    ~A() {
        std::cout << std::this_thread::get_id()
        << " " << __FUNCTION__
        << "(" << (void *)this << ")"
        << std::endl;
    }
    
    // 线程中，第一次使用前初始化
    void doSth() {
    }
};

thread_local A a;

int main() {
    a.doSth();
    std::thread t([]() {
        std::cout << "Thread: "
        << std::this_thread::get_id()
        << " entered" << std::endl;
        a.doSth();
    });
    
    t.join();
    
    return 0;
}
\end{lstlisting}

运行结果
\begin{lstlisting}
$> g++ -std=c++11 -o debug/tls.out ./thread_local.cpp
$> ./debug/tls.out
01 A(0xc00720)
Thread: 02 entered
02 A(0xc02ee0)
02 ~A(0xc02ee0)
01 ~A(0xc00720)
$>
\end{lstlisting}

各线程的\verb|thread_local|变量是如何初始化的？

各线程的\verb|thread_local|变量在初始化之后拥有怎样的生命周期，特别是被声明为\verb|thread_local|的本地变量(local variables)？

在下面的代码示例中，分别有一个全局的\verb|thread_local int g_n = 1|

和一个在函数体内部的\verb|thread_local int i=0|
\begin{lstlisting}
#include <thread>

thread_local int g_n = 1;

void f()
{
    g_n++;
    printf("id=%d, n=%d\n", std::this_thread::get_id(),g_n);
}

void foo()
{
    thread_local int i=0;
    printf("id=%d, n=%d\n", std::this_thread::get_id(), i);
    i++;
}

void f2()
{
    foo();
    foo();
}

int main()
{
    g_n++; 
    f();    
    std::thread t1(f);
    std::thread t2(f);
    t1.join();
    t2.join();
    
    f2();
    std::thread t4(f2);
    std::thread t5(f2);
    t4.join();
    t5.join();
    return 0;
}
\end{lstlisting}

多线程的代码，线程的运行顺序可能不同
\begin{lstlisting}
g++ -std=c++17 -g -lpthread -fPIC -Wall main.cpp && ./a.out
id=8004, n=3  // 主线程，构建了g_n的一个副本，初值=1，两次自增=3

id=8008, n=2  // 线程t1，构建g_n的另一个副本，初值=1，一次自增=2
id=8012, n=2  // 线程t2，构建g_n的另一个副本，初值=1，一次自增=2

id=8004, n=0  // 主线程，构建i的一个副本，初值=0，i为thread_local的本地变量
id=8004, n=1  // 主进程，f2中i被没有被回收，继续被使用，与被static修饰的静态本地变量类似

id=8016, n=0  // t4进程，构建i的另一个副本，初值=0
id=8016, n=1  // t4进程，继续使用i，与静态本地变量相比，thread_local修饰的本地变量每个线程具有一个副本

id=8020, n=0  // t5进程，与t4结果一致
id=8020, n=1
\end{lstlisting}

\section{(未完成)针对异常(exception)规范的修改}

\section{右值引用与移动语义(move semantics)}
\subsection{(C++11)右值引用}
C++ Primer Plus (6th edition)原书第801页，第18.1.9节

\subsubsection{左值引用}
传统的C++引用(也可以称为左值引用)，使得标识符关联到左值。

\subsubsection{什么是左值}
左值是一个表示数据的表达式(比如，变量名或解除引用的指针)，程序可以获取其地址。

左值可以出现在赋值语句的左边。

左值是具有变量名的，可以前置自增自减，可以赋值或复合赋值运算，可以解引用。

使用\verb|const|修饰符，可以声明一种，不能给他赋值的标识符(即，不能出现在赋值语句的左边)，但是仍然可以获取其地址。

\begin{lstlisting}
int n;
int * pt = new int;
const int b = 101;  // 不能给b赋值，但是&b是存在的
int & rn = n;  // rn是n的左值引用，n存储着地址&n处的数据
int & rt = *pt;  // *pt存储着地址pt处的数据
const int * rb = b;  // 
\end{lstlisting}

\subsubsection{右值引用的基本形式}
C++11新增了右值引用，使用\verb|&&|表示。

右值引用可以关联到右值。

\subsubsection{什么是右值}
右值只能出现在赋值表达式右边，但是不能对其使用地址运算符的值。(注意，左值是既能放在左边也能放在右边)

右值包含字面常量、诸如\verb|x + y|这样的表达式、以及函数返回值(但是前提是该函数的返回值不能是引用)

\begin{lstlisting}
int x = 10;
int y = 23;
int && r1 = 13;
int && r2 = x + y;
double && r3 = std::sqrt(2.0);
\end{lstlisting}

\verb|r2|关联的是当时的表达式\verb|x + y|的结果。即使后面修改了\verb|x|或者\verb|y|的值，也不会影响到\verb|r2|

将右值关联到右值引用，使得该右值被存储到特定的位置，并且可以获取该位置的地址。也就是说，在上面的代码示例中，虽然不能将运算符\verb|&|用于13，但是可将其用于\verb|r1|。

引入右值引用的主要目的之一，是实现\textbf{移动语义(move semantics)}

\subsubsection{左值与右值之间的转化}
将左值转换为右值，可以使用\verb|static_cast<>|

\begin{lstlisting}
int a = 0;
int &&k1 = a;  // 编译失败
int &&k2 = static_cast<int&&>(a);  // 编译成功
\end{lstlisting}

也可以使用\verb|std::move|

\begin{lstlisting}
static_cast<std::remove_reference<decltype(arg)>::type&&>(arg)
\end{lstlisting}

万能引用和引用折叠(reference collapsing rule)，也有称之为通用引用(universal reference)。

\begin{lstlisting}
template<class T>
void bar(T && t){}
\end{lstlisting}

这里的t就是一种万能引用，他既可以接收左值引用，也可以接收一个右值引用。万能引用遵循的规则，叫做引用折叠。万能引用的主要功能是，实现完美转发。

\subsection{值类型(value categories)}\label{值类型(value categories)}
\textbf{值类型(value category)}，是C++11引入的概念。

每个C++表达式，都有2个范畴，类型(type category)和值类型(value category)。

\subsubsection{分类}
值类型，任何表达式，都属于以下3个值类型中的一种。
\begin{enumerate}
    \item 纯右值(prvalue, pure rvalue)
    \item 将亡值(xvalue, expiring value)
    \item 左值(lvalue)
\end{enumerate}

\paragraph{为什么要引入值类型}
右值或者左值，其实本质上都是地址空间上的一段内存，在物理层的表达上并无不同。其区别在于开发者想要如何使用这段内存，这里所说的使用，更细致的来说是移动这段内存。

引入值类型，就是为了告诉编译器，当前使用的地址，是一个普通的具名变量的地址，还是一个即将移动离开此处的地址。如果是后者，则可以执行移动语义，直接把它的内容、资源给搬走，不用深拷贝。

\paragraph{区分}
\begin{table}[htbp]\centering
    \caption{} \label{}
    \begin{tabular}{|l|l|l|}
        \hline
        值类型          & 是否具有身份(可以取地址) & 是否可以移动 \\ \hline
        左值(lvalue)   & 可以            & 不可以    \\ \hline
        将亡值(xvalue)  & 可以            & 可以     \\ \hline
        纯右值(prvalue) & 不可以           & 可以     \\ \hline
    \end{tabular}
\end{table}

\subsubsection{纯右值}
纯右值(prvalue, pure rvalue)包括字面值(包括算术表达式、逻辑表达式等表达式)，返回非引用类型的函数调用(就是按值返回的情况，返回的对象是匿名的)，后置自增自减也是纯右值(所以\verb|i++ = 100|这样的语句会报错)。

右值(rvalue)，包括纯右值(prvalue)和将亡值。an rvalue is a prvalue or an xvalue(from cppreference)

初始化了一个对象，这样的纯右值，称之为，获得了一个结果对象(such prvalue is said to have a result object)。

结果对象，应该是一个变量，一个由\verb|new|表达式创建的对象，由临时实例化创建的临时对象(或者其成员)。

每个类和数组纯右值都有一个结果对象，除非它是\verb|decltype|的操作数。

\subsubsection{将亡值}
将亡值(xvalue, expiring value)，指的是其资源可被回收利用的对象，就是说他的内存即将被回收。将亡值首先一定是一个左值。将亡值是与移动语义是相关的值类型。将亡值，一般指的是，即将触发调用移动构造函数或移动赋值构造函数的值，这些函数调用返回后就会消失的值。

泛左值(glvalue, generalized lvalue)包括左值和将亡值。

\subsubsection{将亡值的举例}
\begin{enumerate}
    \item 返回类型是右值引用的函数，其返回值，是将亡值。(Function call with return type rvalue reference is xvalue)
    \item 对象表达式
    \item 向右值引用转换的cast表达式
    \item 运算符表达式，结果的返回类型，是指向对象的右值引用
\end{enumerate}
\begin{lstlisting}
#include <type_traits>

template <class T> struct is_prvalue : std::true_type {};
template <class T> struct is_prvalue<T&> : std::false_type {};
template <class T> struct is_prvalue<T&&> : std::false_type {};

template <class T> struct is_lvalue : std::false_type {};
template <class T> struct is_lvalue<T&> : std::true_type {};
template <class T> struct is_lvalue<T&&> : std::false_type {};

template <class T> struct is_xvalue : std::false_type {};
template <class T> struct is_xvalue<T&> : std::false_type {};
template <class T> struct is_xvalue<T&&> : std::true_type {};

// Example from C++23 standard: 7.2.1 Value category [basic.lval]
struct A
{
    int m;
};

A&& operator+(A, A);
A&& f();

int main()
{
    A a;
    A&& ar = static_cast<A&&>(a);
    
    // Function call with return type rvalue reference is xvalue
    static_assert(is_xvalue<decltype( (f()) )>::value);
    
    // Member of object expression, object is xvalue, `m` is a non-static data member
    static_assert(is_xvalue<decltype( (f().m) )>::value);
    
    // A cast expression to rvalue reference
    static_assert(is_xvalue<decltype( (static_cast<A&&>(a)) )>::value);
    
    // Operator expression, whose return type is rvalue reference to object
    static_assert(is_xvalue<decltype( (a + a) )>::value);
    
    // Expression `ar` is lvalue, `&ar` is valid
    static_assert(is_lvalue<decltype( (ar) )>::value);
    [[maybe_unused]] A* ap = &ar;
}
\end{lstlisting}

\subsubsection{左值}
左值(lvalue)，指的是泛左值中不是将亡值的那一部分泛左值。an lvalue is a glvalue that is not an xvalue(from cppreference)

由于左值尝尝出现在赋值运算符(也就是等于号)的左侧，所以最早有了左值这个名字。但是这并不是判断左值的条件。见下面的代码示例。

\verb|const int &c{a}; c = 6;|，这里变量\verb|c|虽然是左值，但是他并不能出现在赋值运算符左侧。

\verb|foo|作为函数指针，他也是左值，但是他也不能出现在赋值运算符左侧。
\begin{lstlisting}
void foo();

void baz()
{
    int a; // Expression `a` is lvalue
    a = 4; // OK, could appear on the left-hand side of an assignment expression
    
    int &b{a}; // Expression `b` is lvalue
    b = 5; // OK, could appear on the left-hand side of an assignment expression
    
    const int &c{a}; // Expression `c` is lvalue
    c = 6;           // ill-formed, assignment of read-only reference
    
    // Expression `foo` is lvalue
    // address may be taken by built-in address-of operator
    void (*p)() = &foo;
    
    foo = baz; // ill-formed, assignment of function
}
\end{lstlisting}

\subsubsection{右值并不一定出现在等于号右侧}
\verb|S{}|虽然是纯右值，但是他也可以出现在等于号左侧。
\begin{lstlisting}
#include <iostream>

struct S
{
    S() : m{42} {}
    S(int a) : m{a} {}
    int m;
};

int main()
{
    S s;
    
    // Expression `S{}` is prvalue
    // May appear on the right-hand side of an assignment expression
    s = S{};
    
    std::cout << s.m << '\n';
    
    // Expression `S{}` is prvalue
    // Can be used on the left-hand side too
    std::cout << (S{} = S{7}).m << '\n';
}
\end{lstlisting}

\subsubsection{值类型}
值类型，是对表达式进行分类的，而不是值本身。注意研读下面cppreference给出的代码示例。
\begin{lstlisting}
#include <type_traits>
#include <utility>

template <class T> struct is_prvalue : std::true_type {};
template <class T> struct is_prvalue<T&> : std::false_type {};
template <class T> struct is_prvalue<T&&> : std::false_type {};

template <class T> struct is_lvalue : std::false_type {};
template <class T> struct is_lvalue<T&> : std::true_type {};
template <class T> struct is_lvalue<T&&> : std::false_type {};

template <class T> struct is_xvalue : std::false_type {};
template <class T> struct is_xvalue<T&> : std::false_type {};
template <class T> struct is_xvalue<T&&> : std::true_type {};

int main()
{
    int a{42};
    int& b{a};
    int&& r{std::move(a)};
    
    // Expression `42` is prvalue
    static_assert(is_prvalue<decltype((42))>::value);
    
    // Expression `a` is lvalue
    static_assert(is_lvalue<decltype((a))>::value);
    
    // Expression `b` is lvalue
    static_assert(is_lvalue<decltype((b))>::value);
    
    // Expression `std::move(a)` is xvalue
    static_assert(is_xvalue<decltype((std::move(a)))>::value);
    
    // Type of variable `r` is rvalue reference
    static_assert(std::is_rvalue_reference<decltype(r)>::value);
    
    // Type of variable `b` is lvalue reference
    static_assert(std::is_lvalue_reference<decltype(b)>::value);
    
    // Expression `r` is lvalue
    static_assert(is_lvalue<decltype((r))>::value);
}
\end{lstlisting}



\subsection{引用折叠，万能引用，std::move}\label{引用折叠，万能引用，std::move}
\subsubsection{引用折叠与万能引用}
引用折叠(reference collapsing)。万能引用，也有称之为通用引用(universal reference)。

指的是右值引用与模板相结合时的情况，对于下面的代码示例来说

\begin{lstlisting}
template<typename T>
void f(T&& param);
\end{lstlisting}

这里的\verb|T&&|，在此时并不确定他传入的是一个什么样值类型的值。而且\verb|param|最终的值类型是什么，也取决于传入时的值类型。如果被左值初始化，那么它就是左值，反之亦然。

这种未定的引用类型，称为\textbf{万能引用}或\textbf{通用引用(universal reference)}。

如果传入的参数是左值引用或者右值引用，那么此时\verb|param|前面发生的引用次数就多于2次了，接下来就会发生引用折叠(reference collapsing)，需要按照一定的规则。

\begin{enumerate}
    \item 所有右值引用折叠到右值引用上仍然是一个右值引用。\verb|A&& &&|变成\verb|A&&|
    \item 所有的其他引用类型之间的折叠都将变成左值引用。\verb|A& &| 变成 \verb|A&|; \verb|A& &&| 变成 \verb|A&|; \verb|A&& &| 变成 \verb|A&|
\end{enumerate}

简单来说，只要有1个左值引用，那么引用折叠的结果就是左值引用。只有2次右值引用，最终才会得到右值引用。

\subsubsection{std::move}
\verb|std::move|可以将任何传入的值变为右值，基本等同于\verb|static_cast<T&&>(lvalue);|

函数原型

\begin{lstlisting}
template< class T >
typename std::remove_reference<T>::type&& move( T&& t ) noexcept;  // (since C++11)
\end{lstlisting}

注意，这里\verb|typename|的含义是告诉编译器，后面紧跟着的\verb|std::remove_reference<T>::type&&|是一个类型名，而不是别的什么东西(比如成员函数或者成员变量)。这是\verb|typename|关键字的用法之一，给编译器提供更多信息，以便于在编译期就暴露出更多问题，而不是遗留到运行期。

返回值

\begin{lstlisting}
static_cast<typename std::remove_reference<T>::type&&>(t)
\end{lstlisting}

函数参数\verb|T&& t|是一个指向模板类型参数的右值引用，因为引用折叠，此参数可以与任何类型的实参匹配。

示例代码\ref{static_cast与std::move}，用\verb|static_cast|复刻了\verb|std::move|的行为。

\begin{code_example}\label{static_cast与std::move}
\begin{lstlisting}
std::vector<int> v0{1, 2, 3};
std::vector<int> v2 = static_cast<std::vector<int> &&>(v0);
std::cout << "after move, v0.size() = " << v0.size() << '\n';

// console output
// after move, v0.size() = 0
\end{lstlisting}
\end{code_example}

\subsubsection{为什么要使用std::move，优势在哪里}
\begin{enumerate}
    \item C++ 标准库使用比如\verb|vector::push_back| 等这类函数时,会对参数的对象进行复制,连数据也会复制.这就会造成对象内存的额外创建, 本来原意是想把参数\verb|push_back|进去就行了,通过\verb|std::move|，可以避免不必要的拷贝操作。
    \item \verb|std::move|是将对象的状态或者所有权从一个对象转移到另一个对象，只是转移，没有内存的搬迁或者内存拷贝所以可以提高利用效率,改善性能.。
    \item 对指针类型的标准库对象并不需要这么做.
\end{enumerate}

\subsubsection{std::remove\_reference}

\begin{lstlisting}
template< class T >
struct remove_reference;  // (since C++11)
\end{lstlisting}

结构体中有成员\verb|type|，如果T是一个引用类型，则\verb|type|是T所引用的数据的类型。如果T不是引用，则\verb|type|就是T的类型。

\subsection{移动语义的作用}
\subsubsection{适用场景}
在通过一个对象，创建另外一个同类型的对象的时候，会发生先创建一个临时对象，将旧值复制给临时对象，再把临时对象复制给新创建的对象(调用了拷贝构造函数)。

这就是右值引用的应用场景，C++为了避免这种额外的复制过程，提升效率，所以提出了右值引用和移动语义技术。

而移动语义技术，可以避免无意义的搬动原始数据，只是修改其“指向”即可，节约了开销。

类似于操作系统中的移动文件，文件的二进制码并没有从磁盘的这个扇区移动到了那个扇区，而是只是修改了文件标识符的指向。

\subsection{如何实现与使用移动语义功能}
要实现移动语义，需要让编译器知道开发者的意图，即，什么时候需要复制，什么时候不需要复制。

常规的拷贝构造函数，将\verb|const|左值引用\verb|const&|作为参数，执行深拷贝

而遵循移动语义的移动构造函数(move constructor)，使用右值引用\verb|&&|作为参数，只修改其“指向”

\subsubsection{为什么移动构造函数不用const右值引用作为参数}
因为将新数据的所有者移交给新标识符时，可能修改其内容，所以不应为\verb|const|的。

\begin{lstlisting}
Useless two = one;
Useless four(one + three);    
\end{lstlisting}

one是左值，与左值引用匹配，所以会调用拷贝构造函数。

one + three是右值，与右值引用匹配，所以会调用拷贝构造函数。

\subsubsection{如果没有定义移动构造函数}
那么会采用老办法，就是通过一个临时的副本。

\subsection{赋值运算符与移动语义}
与拷贝构造函数和移动构造函数相类似，赋值运算符也有类似的对应关系。

拷贝赋值运算符详见第\pageref{拷贝赋值运算符(copy assignment operator)}页第\ref{拷贝赋值运算符(copy assignment operator)}节，移动赋值运算符详见第\pageref{移动赋值运算符(move assignment operator)}页第\ref{移动赋值运算符(move assignment operator)}节。

\subsection{完美转发, std::forward}
\verb|std::forward|的作用是，保证原来的值的左值性或右值性保持不变

\section{对类的修改}
\subsection{默认的方法和禁用的方法 default和delete关键字}
\subsubsection{关键字default}
使用关键字\verb|default|可以显式地声明特殊成员函数的默认版本。

(特殊成员函数，以前指的是4个，默认构造函数，拷贝构造函数，拷贝赋值运算符，默认析构函数，C++11又新增了两个，移动构造函数，移动赋值运算符，一共6个)

禁用编译器自动生成的默认特殊成员函数，可以采用把声明放在\verb|private|区域的方式，也可以使用关键字\verb|delete|

\subsubsection{关键字delete}
关键字\verb|delete|也可以用来禁止特定的转换

比如下面的代码示例，如果有\verb|className.work(2)|这样的语句，编译器会视其为调用错误。

\begin{lstlisting}
void work(int) = delete;
\end{lstlisting}

\subsection{(C++11)委托构造函数}
\subsubsection{委托构造函数}
C++11允许在一个构造函数的定义中使用另一个构造函数，这种被称为委托(delegating)

某个类型的一个构造函数可以委托同类型的另一个构造函数对对象进行初始化。为了描述方便我们称前者为委托构造函数，后者为目标构造函数。

委托构造函数会将控制权交给目标构造函数，在目标构造函数执行完之后，再执行委托构造函数的主体。

委托构造函数的语法非常简单，只需要在委托构造函数的初始化列表中调用目标构造函数即可。

代码示例
\begin{lstlisting}
class Person {
    public:
    // 非委托构造函数使用对应的实参初始化成员
    Person(std::string _name, int _age, double _income):
        name(_name), age(_age), income(_income) { }
    // 其余构造函数全都委托给另一个构造函数
    Person(): Person("", 0, 0) {}
    Person(std::string _name): Person(mike,0,0) {}
    Persona(std::string _name, int _age,): Person(mike,20,0){}
};
\end{lstlisting}

\begin{lstlisting}
#include <iostream>
class Data 
{
public:
    int num1;
    int num2;
    Data()    //目标构造函数
    {
        num1 = 100;
    }
    Data(int num) : Data()  //委托构造函数
    { // 委托 Data() 构造函数
        num2 = num;
    }
};
void function()
{
    Data data(99);  //首先调用Data() 先给num1复制，然后走到Data(int num) : Data() ，给num2赋值
    
    std::cout <<data.num1 << std::endl;
    std::cout <<data.num2 << std::endl;
}

// 输出
// 100
// 99
\end{lstlisting}

\subsubsection{委托构造函数与成员初始化列表}
委托构造函数还可以与成员初始化列表联用。

为了使用委派构造时还能使用初始化列表，我们可以定义一个\verb|private| 的目标构造函数，并将初始化列表放在这个\verb|private|的目标构造函数中，这样其他委托构造函数就可以通过委派这个目标构造来实现构造的功能。

\begin{lstlisting}
class Person
{
public:
    Person() :Person(1, 'a') {}
    Person(int i) : Person(i, 'a') {}
    Person(char ch) : Person(1, ch) {} 
    
private:
    Person(int i, char ch) :type(i), name(ch) {/*其他初始化信息*/}
    int type{ 1 };
    char name{ 'a' };
};
\end{lstlisting}

另一种形式
\begin{lstlisting}
// 通过委托构造函数进行优化
class Person
{
public:
    Person()
    {
        InitPerson();
    }
    Person(int i) : Person()
    {
        type = i;
    }
    Person(char ch) : Person() // 委托构造函数不能使用初始化列表 初始化成员变量
    {
        name = ch;
    }
    
private:
    void InitPerson() { /* 其他初始化 */}
    int type{ 1 };
    char name{ 'a' };
};
\end{lstlisting}

\subsubsection{注意事项，防止出现环形委托}
可能存在一个构造函数，它既是委托构造函数也是代理构造函数，要防止委托环的出现，否则导致构造死循环。

如果一个构造函数为委托构造函数，那么其初始化列表里就不能对数据成员和基类进行初始化，构造函数不能同时委派和使用初始化列表。所以如果委派构造函数要给变量赋初值，初始化代码必须放在函数体中。

\subsection{(C++11)继承构造函数}
C++11让派生类继承基类的所有构造函数(默认、复制、移动构造函数除外，他们属于特殊成员函数)，除了那些与派生类构造函数的特征标匹配的基类构造函数。也就是说，如果特征标与基类和派生类的构造函数同时匹配的话，还是会使用派生类的构造函数。

继承的基类构造函数，只能初始化派生类的基类部分，派生类独有的那部分，不会初始化。如果需要初始化派生类成员，则应该使用成员列表初始化语法。

继承构造函数是 C++11 引入的一个特性，它允许派生类通过简单的声明来继承一个或多个基类的构造函数。这意味着派生类可以直接使用基类的构造函数来初始化其继承的基类部分，而不需要在派生类中显式地重写或复制这些构造函数的初始化代码。

这个特性的主要目的是减少代码重复，并提高代码的可读性和可维护性。通过继承构造函数，派生类可以清晰地表明它继承了基类的哪些构造函数，这使得类的继承关系更加明确。

\subsubsection{单继承的情况}
使用\verb|using|语句，来在派生类里引入基类的\textbf{所有的}构造函数。
\begin{lstlisting}
// 基类 A
class A {
public:
    A(int i) { std::cout << "Constructor of class A called with " << i << std::endl; }
    A(double d) { std::cout << "Constructor of class A called with " << d << std::endl; }
};

// 派生类 B
class B : public A {
public:
    // 继承构造函数
    using A::A;
};

int main() {
    B b1(10);  // 调用 A 的 int 构造函数
    B b2(20.5); // 调用 A 的 double 构造函数
    return 0;
}
\end{lstlisting}

\subsubsection{多继承的情况}
\begin{lstlisting}
// 基类 A
class A {
public:
    A(int i) { std::cout << "Constructor of class A called with " << i << std::endl; }
};

// 基类 B
class B {
public:
    B(double d) { std::cout << "Constructor of class B called with " << d << std::endl; }
};

// 派生类 C
class C : public A, public B {
public:
    // 继承构造函数
    using A::A;
    using B::B;
};

int main() {
    C c1(10);  // 调用 A 的 int 构造函数
    C c2(20.5); // 调用 B 的 double 构造函数
    return 0;
}
\end{lstlisting}

\subsection{虚说明符override和final}
\subsubsection{虚说明符override}
\begin{lstlisting}
class Action
{
    virtual void f(char ch) const;
}

class Bingo : public Action
{
    virtual void f (char * ch) const;
}
\end{lstlisting}

由于派生类\verb|Bingo|中定义的\verb|f()|是\verb|f(char *)|而不是\verb|f(char)|，所以程序以后只能运行\verb|f(char *)|，\verb|f(char)|将会被隐藏。

可以使用虚说明符\verb|override|来明确地指出，我确实想要覆盖某一个虚函数，而不是把旧的隐藏而声明一个新的。

\begin{lstlisting}
virtual void f (char * ch) const override {...}
\end{lstlisting}

如果像上面一样使用，编译器会报错，因为基类里的虚函数是\verb|f(char)|，而不是\verb|f(char *)|

\subsubsection{虚说明符final}
如果禁止派生类覆盖某个虚方法，则可以在参数列表后面加上说明符\verb|final|

\verb|override|和\verb|final|不是关键字，他们的特殊含义只在特定的上下文中才能体现。在其他上下文中，他们仍可以作为常规标识符使用。

\subsubsection{重写，重载与隐藏}
\begin{itemize}
    \item 重载(overload)，函数名相同，但是函数签名不同，有不同的参数列表
    \item 重写(override)，更接近于覆盖，指派生类覆盖了基类的虚函数
    \item 隐藏(overwrite)，派生类如果出现了与基类相同名称的函数，如果函数签名不相同，则基类函数会被隐藏，如果函数签名相同，如果基类函数是虚函数，则这里发生的就是重写，否则基类函数也会被隐藏。
\end{itemize}

为了防止，程序员的疏忽，导致了并不想重写的时候发生了重写，C++标准提出了\verb|override|说明符，指出，基类中的这个函数，在派生类中，必须要是重写，而不能是其他的情况，不构成重写也会编译期报错。一般要求，在有重写意图的函数后面，都要加上\verb|override|，让编译器来帮忙检查是否构成了重写。

\verb|final|说明符，与\verb|override|说明符的功能正好相反，final的含义是基类的这个函数不允许被重写。

都是放在参数列表圆括号和函数体花括号之间的位置。

\subsection{显式转换运算符}

C++在11版本之前引入了\verb|explicit|关键字，以禁止单参数构造函数造成的自动转换。

\begin{lstlisting}
class Plebe
{
    Plebe(int);
    explicit Plebe(double);
};

...
Plebe a, b;
a = 5;  // 隐式类型转换，调用Plebe(5)
b = 0.5;  // 不允许
b = Plebe(0.5);  // 显式类型转换
\end{lstlisting}

C++11拓展了\verb|explicit|的用法，使得可以对转换函数也做类似的处理。

\subsection{类内成员初始化(in-class initialization)}
在类定义中直接初始化成员，可以避免在构造函数中编写重复的代码，降低工作量。

C++11的类内初始化，等价于在构造函数中使用成员初始化列表。为成员分配内存仍然是在创建对象时完成的。

如果创建对象的构造函数时也使用了成员初始化列表，则构造函数中的成员初始化列表值\textbf{将覆盖类内初始化的默认值}。
\begin{lstlisting}
class A{
private:
    int a_ = 0;
    double b_ = 0.;
    const float c_ = 0.f;
    long& d_;
public:
    A(int, double, float, long&);
};

\end{lstlisting}



\section{模板和STL方面的修改}
\subsubsection{新的STL容器}

C++11新增了容器

\begin{itemize}
\item \verb|forward_list|，一种单向链表，只能沿一个方向遍历。
\item \verb|unordered_map|
\item \verb|unordered_multimap|
\item \verb|unordered_set|
\item \verb|unordered_multiset|
\item \verb|array|
\end{itemize}

其他四个以\verb|unordered_|开头的容器，都是使用哈希表实现的。

\subsubsection{新的STL方法}

\begin{itemize}
\item \verb|cbegin()|
\item \verb|cend()|
\item \verb|crbegin()|
\item \verb|crend()|
\end{itemize}

返回一个迭代器，指向容器的第一个元素和最后一个元素的后面，而且将元素视为\verb|const|

类似地，\verb|crbegin()|和\verb|crend()|是对应的\verb|const|版本

\subsubsection{valarray升级}

模板\verb|valarray|是独立于STL开发的，之前无法适配于STL的设计思想。C++11为它新增了一些函数，使得它也引入了STL思想。

\subsubsection{抛弃export}

C++98新增了关键字\verb|export|，让程序员能够将模板定义放在接口文件和实现文件中，其中，前者包含原型和模板声明，而后者包含模板函数和方法的定义。

C++11终止了这种用法，但是仍保留了关键字\verb|export|。

\subsubsection{尖括号的问题}
以前C++要求在声明嵌套模板的时候使用空格将两个连续的尖括号分开，C++11不再这样要求了。
\begin{lstlisting}
std::vector<std::list<int> > v1;  // 旧写法
std::vector<std::list<int>> v1;  // 新写法
\end{lstlisting}

\subsubsection{std::common\_type}
定义于头文件\verb|<type_traits>|
\begin{lstlisting}
template< class... T >
struct common_type;
\end{lstlisting}

应用场景是，参数包\verb|T...|中传入了许多类型。

确定所有类型 \verb|T...| 的共用类型，即所有 \verb|T...| 都能隐式转换到的类型。若这种类型(根据后述规则确定)存在，则成员 \verb|type| 指名该类型。否则，无成员 \verb|type| 。

\section{编译时断言static\_assert}
用于在编译时期检查程序中的特定条件是否为真。如果条件为假，编译器将产生一个编译错误，并显示开发者提供的错误消息。

基本语法很简单
\begin{lstlisting}
static_assert(常量表达式，提示字符串)
\end{lstlisting}

常量表达式，必须是在编译期就能计算出结果的表达式，不能是只能在运行时得到结果的表达式。

\subsection{使用方法}
\subsubsection{类型检查}
确保在编译时就发现类型不匹配的问题
\begin{lstlisting}
template <typename T>
class Container {
    static_assert(std::is_integral<T>::value, "T must be an integral type");
    // 类的实现...
};
\end{lstlisting}

\subsubsection{常量表达式检查}
确保某个常量表达式的值符合预期。这在定义一些依赖于特定数值的算法或配置时非常有用。
\begin{lstlisting}
constexpr size_t BufferSize = 1024;
static_assert(BufferSize % 16 == 0, "BufferSize must be a multiple of 16");
\end{lstlisting}

\subsubsection{模板参数检查}
检查模板参数是否符合预期。这可以防止不正确的模板参数导致编译或运行时错误。
\begin{lstlisting}
template <typename T>
class MyClass {
    public:
    static_assert(std::is_floating_point<T>::value, "T must be a floating point type");
    // 类的实现...
};
\end{lstlisting}

\subsubsection{接口兼容性检查}
在面向对象编程中，\verb|static_assert| 可以确保派生类正确地实现了基类的所有虚函数，从而保证了接口的一致性。
\begin{lstlisting}
class Base {
    public:
    virtual void doSomething() = 0;
    // 其他虚函数...
};

class Derived : public Base {
    public:
    void doSomething() override {
        // 实现...
    }
    // 其他虚函数的实现...
};

static_assert(std::is_base_of<Base, Derived>::value, "Derived must be derived from Base");
\end{lstlisting}

C++11在头文件\verb|<type_traits>|中添加了\verb|std::is_base_of|，用于判断两个类型的继承关系。基本的使用方法是
\begin{lstlisting}
std::is_base_of<A, B>::value; // get true if B is derived from A
\end{lstlisting}

\subsubsection{避免重复代码}
用于避免在多个函数或模板中重复相同的类型检查或其他断言，从而提高代码的可维护性。
\begin{lstlisting}
template <typename T>
void function(T param) {
    static_assert(std::is_integral<T>::value, "T must be an integral type");
    // 函数实现...
}

template <typename T>
void anotherFunction(T param) {
    static_assert(std::is_integral<T>::value, "T must be an integral type");
    // 另一个函数的实现...
}
\end{lstlisting}

\subsection{static\_assert和assert的区别}
\begin{enumerate}
    \item 断言时机：\verb|static_assert|是在编译时进行断言检查，如果条件不满足，编译器会报错并终止编译过程。而\verb|assert|是在运行时进行断言检查，如果条件不满足，程序会在运行时打印错误信息并通常终止执行。
    \item 性能影响：\verb|static_assert|不会产生任何运行时开销，因为它在编译时就已经完成了检查。\verb|assert|会在运行时检查条件，因此会对性能产生影响，且在发布版中通常会被禁用。
    \item 错误信息：\verb|static_assert|可以提供详细错误信息，它允许一个字符串作为第二个参数，这个字符串会在编译错误时显示。\verb|assert|在运行时提供错误信息，但通常不如\verb|static_assert|提供的信息详细和及时。
    \item 使用场景：\verb|static_assert|常用于模板编程中，确保类型特性或编译时常量表达式满足特定条件。而\verb|assert|用于运行时检查程序逻辑，如缓冲区越界、非法参数等。
    \item 强制性：\verb|static_assert|是强制性的，如果条件不满足，程序无法编译。\verb|assert|可以通过预处理器定义(如NDEBUG)来禁用，使其在某些编译配置下不产生任何代码。
\end{enumerate}

\begin{itemize}
    \item \verb|static_assert|的优势：提供编译时检查，有助于及早发现错误。不影响运行时性能。可以检查类型特性，适合模板编程。可以提供编译时的错误信息，方便调试。
    \item \verb|static_assert|的劣势：只能检查编译时常量表达式，灵活性较低。对于某些运行时才能确定的条件无法检查。
    \item \verb|assert|的优势：可以检查运行时状态，如变量值、函数返回值等。有助于调试，可以快速定位运行时错误。
    \item \verb|assert|的劣势：运行时检查，对性能有影响。在发布版中通常会被禁用，因此不适用于需要强制检查的场景。错误信息通常不如\verb|static_assert|详细。
\end{itemize}

\subsection{(C++17)单参数static\_assert，省略提示字符串，编译期也能提供基本的错误信息。}
单参数\verb|static_assert|，省略第二个参数错误诊断字符串。一般来说，希望错误诊断字符串就是常量表达式的内容，这样可以直观地看出是哪个代码出了错误。可以用宏来实现
\begin{lstlisting}
#define LAZY_STATIC_ASSERT(B) static_assert(B, #B)
\end{lstlisting}

而C++17开始优化了\verb|static_assert|，可以直接省略第二个参数了，也不需要宏来实现。











\section{lambda表达式}
\subsection{lambda表达式}
\subsubsection{函数对象(function object)}
\begin{enumerate}
    \item 函数指针(function pointers)
    \item 函数对象(functors)
    \item lambda函数(lambdas)
\end{enumerate}

函数指针，函数名去掉圆括号，就是函数指针了

函数对象，重载\verb|operator()()|了的类。

\subsubsection{lambda表达式}
lambda表达式，是一种匿名函数，其基本形式可以简单记忆为\verb|[](){}|。

在C++11中，对于接收函数指针和函数对象作为参数的函数，都可以使用lambda表达式作为其参数(新增了一种选择)

\subsubsection{基本语法}
\begin{lstlisting}
[captures](parameters)specifiers exceptions -> ret{ body }
\end{lstlisting}

\begin{enumerate}
    \item \verb|captures|捕获列表，捕获函数当前作用域内的1个或多个变量，按值或按引用去使用他们。
    \item \verb|parameters|可选参数列表，语法与普通函数的参数列表一样。
    \item \verb|specifiers|可选限定符，C++11可以使用mutable
    \item \verb|exceptions|可选异常说明符，可以使用noexcept
    \item \verb|ret|是可选返回类型，可以使用返回类型后置，如果函数没有返回值，则可以直接省略\verb|->ret|部分。如果lambda表达式不包含\verb|return|语句，则认为返回类型是\verb|void|。
    \item \verb|body|是函数体
\end{enumerate}

\begin{lstlisting}
bool f3(int x) {return x % 3 == 0;}

[](int x) {return x % 3 == 0;}
\end{lstlisting}

\subsubsection{自动类型推断}
当且仅当lambda表达式完全由一条\verb|return|语句组成时，自动类型推断才管用，否则需要使用返回类型后置语法。

\begin{lstlisting}
[](double x)->double{int y = x; return x – y;}
\end{lstlisting}

\subsubsection{lambda函数的优势}

\begin{enumerate}
    \item 距离。函数的定义与函数使用的地方越近越好。
    \item 简洁。可以给lambda表达式指定一个名称，并在其他地方使用。
    \item 效率。与编译器内联有关，函数指针是不内联的，而函数对象和lambda表达式允许内联。
    \item 功能。下面叙述。
\end{enumerate}

\subsubsection{捕获列表 - lambda表达式如何访问变量}
lambda表达式，可以访问其所在作用域内的任何动态变量，具有极高的灵活性，而不需要严格地再去维护函数的参数列表这样的工作。

\begin{enumerate}
    \item \verb|[this]|，捕获\verb|this|指针。
    \item \verb|[varName]|，按值访问某个变量。
    \item \verb|[&varName]|，按引用访问变量。
    \item \verb|[&]|，捕获全部变量的引用，按引用访问所有动态变量，包括\verb|this|指针。
    \item \verb|[=]|，捕获全部变量的值，按值访问所有动态变量，包括\verb|this|指针。(C++20之后会告警，捕获\verb|this|指针编译器会建议使用\verb|[=, this]|)
\end{enumerate}

而且这些方式可以灵活组合，比如\verb|[ted, &ed]|可以按值访问变量\verb|ted|、按引用访问变量\verb|ed|。

\subsubsection{lambda表达式是如何捕获变量的}
以下面的代码示例为例，编译器看到\verb|[&]|之后，开始在AST中的当前作用域下搜索所有的局部变量，然后构造一个匿名结构体，存储他们的引用。

这里也有编译器优化的可能性，就是对于未被lambda表达式使用的变量，不会添加到匿名结构体当中。
\begin{lstlisting}
{
    int a = 10;
    std::string s = "hello";
    auto f = [&](){
        s += std::to_string(a);
    };
    f();
    std::cout << s;
}    
\end{lstlisting}

等价于下面的代码示例
\begin{lstlisting}
{
    int a = 10;
    std::string s = "hello";
    
    struct _lambda_expression {
        int &a;
        std::string &s;
        _lambda_expression(int &a, std::string &s) : a(a), s(s) {}
        void operator()() {
            s += std::to_string(a);
        }
    };
    
    f();
    std::cout << s;
}    
\end{lstlisting}

根据不同的情况，\verb|operator()|可能带有\verb|const|。

\subsection{C++11之后的lambda表达式新特性}
\subsubsection{(C++14)广义捕获}
见下面的写法。
\begin{lstlisting}
int x = 5;
auto foo = [r = x + 1]{return r;};
\end{lstlisting}

捕获变量\verb|x|，并且将他直接转换成\verb|r|并且在lambda表达式内部使用。

\subsubsection{(C++14)基于auto的泛型lambda表达式}
C++14支持泛型lambda表达式，并没有借助模板，而是使用\verb|auto|占位符完成的。

\begin{lstlisting}
auto foo = [](auto a){return a;};
\end{lstlisting}

\subsubsection{(C++17)新增[*this]捕获this指针}
\verb|[*this]|的目标是拷贝\verb|this|对象，在lambda表达式中使用拷贝的对象，而不是原始对象。

\verb|[=, this]|则是直接使用原始对象，与\verb|[=]|起到的作用是相同的。

\verb|[*this]|的语法让程序生成了一个this对象的副本并存储在lambda表达式内，可以在lambda表达式内直接访问这个复制对象的成员，消除了之前lambda表达式需要通过临时对象访问对象成员的尴尬。

\subsubsection{(C++20)新增[=, this]}
C++20开始，编译器会对\verb|[=]|隐式捕获\verb|this|指针的情况发出警告了，并且建议程序员使用\verb|[=, this]|

\subsubsection{(C++20)基于模板的泛型lambda表达式}
\begin{lstlisting}
auto f = []<typename T>(std::vector<T> vector){...}
\end{lstlisting}

\begin{lstlisting}
template<typename... Args>
auto createLambda(Args... args) {
    return [...args = std::move(args)]() {
        // 在函数体中使用args...
    };
}
\end{lstlisting}

\subsubsection{(C++20)可构造和可赋值的无状态的lambda表达式}
\begin{itemize}
    \item \textbf{有状态的lambda表达式}：捕捉上下文对象形成闭包的lambda表达式，形成闭包。
    \item \textbf{无状态的lambda表达式}：不捕捉上下文对象
\end{itemize}

lambda表达式如果捕获外部对象，则形成了对外部环境的以来，形成了\textbf{闭包}。而闭包这个现象，本身又要求其必须带有构造和拷贝。而C++20之前的lambda表达式，是没有构造和拷贝过程的，这阻碍了许多应用的实现。

%C++11标准中新增了 lambda 表达式，可以快速精简的方式构建函数对象，而且还支持闭包，可以简单方便的捕捉当前上下文的对象。由于闭包的存在，又反过来要求闭包本身不可缺省构造和拷贝(拷贝和移动构造函数标记为 deleted )，这样和普通函数对象有区别，影响其应用。

因此在C++20中，将有状态的 lambda (捕捉上下文对象形成闭包)和无状态的 lambda (不捕捉上下文对象)区分开来，然后放宽无状态 lambda 的限制，允许无状态 lambda 有缺省的构造函数和拷贝、移动构造函数，这样无状态 lambda 就更加像一个函数对象，可以在其他普通函数对象可以使用的场合中使用。

像\verb|std::sort|和\verb|std::find_if|这样的函数需要一个函数对象或函数指针来辅助排序和查找，这种情况我们可以使用lambda表达式完成任务。但是如果遇到\verb|std::map|这种容器类型就不好办了，因为\verb|std::map|的比较函数对象是通过模板参数确定的

\begin{lstlisting}
auto greater = [](auto x, auto y){return x > y;};
std::map<std::string, int, decltype(greater)> mymap;

auto greater = [](auto x, auto y) { return x > y; };
std::map<std::string, int, decltype(greater)> mymap1, mymap2;
mymap1 = mymap2;
\end{lstlisting}

\section{包装器(wrapper)，适配器(adapter)}
\subsection{包装器的概念}
包装器(wrapper)，也叫做适配器(adapter)

\verb|std::bind1st|和\verb|std::bind2nd|

C++11还提供了模板\verb|std::bind|，\verb|std::mem_fn|，\verb|std::reference_wrapper|以及包装器\verb|std::function|

模板\verb|std::bind|可以替代\verb|std::bind1st|和\verb|std::bind2nd|

模板\verb|std::mem_fn|可以将成员函数作为常规函数进行传递

模板\verb|std::reference_wrapper|能够创建行为类似于引用但是可被复制的对象

包装器\verb|std::function|可以以统一的方式处理多种类似于函数的形式

可调用对象(callable object)概念，除了包括3种函数对象以外，还包括一个就是传统函数。

\subsection{包装器解决的问题}

模板在面对可调用对象时，会重复地生成许多实例，虽然他们都实现相同的功能。为了减少这些重复开销，所以提出了包装器\verb|std::function|

头文件\verb|<functional>|

\subsection{调用特征标(call signature)}

可调用对象的形式不同，但是其共同点都是，传入double类型，返回double类型，即他们都符合“double(double)”这样的调用特征标(call signature)

模板\verb|std::function|从调用特征标的角度，定义了一个对象，用于包装一切特征标相同的可调用对象。

\subsection{包装器的使用方法}
\subsubsection{std::bind}
详见第\ref{(C++11)std::bind}节

\subsubsection{std::function}
详见第\ref{std::function}节

\subsection{std::mem\_fn}
\verb|std::mem_fn|的优点是，可以直接使用其他类公共函数，不需要自行编写。

他是不能用于全局静态函数的，必须用于类或者结构体的成员函数。
\begin{lstlisting}
#include <functional>
#include <iostream>

struct Foo {
    void display_greeting() {
        std::cout << "Hello, world.\n";
    }
    void display_number(int i) {
        std::cout << "number: " << i << '\n';
    }
    int data = 7;
};

int main() {
    Foo f;
    
    auto greet = std::mem_fn(&Foo::display_greeting);
    greet(f);
    
    auto print_num = std::mem_fn(&Foo::display_number);
    print_num(f, 42);
    
    auto access_data = std::mem_fn(&Foo::data);
    std::cout << "data: " << access_data(f) << '\n';
}
\end{lstlisting}

\subsection{std::reference\_wrapper}
该模板类仿真一个 T类型对象的引用。但行为与普通的引用不太一样。
用类型T实例化的\verb|reference_wrapper|类能包装该类型的一个对象，并产生一个\verb|reference_wrapper<T>|类型的对象，该对象就像是一个引用，与普通引用最大的不同是：该引用可以考贝或赋值。

通过\verb|get()|方法获取到\verb|reference_wrapper|所引用的对象，从而进行修改其值等操作。

\begin{lstlisting}
int a = 10;
reference_wrapper<int> r1 = a;
cout << r1 << endl;
cout << r1.get() << endl;
r1.get() = 100;
cout << r1.get() << endl;

// 输出
// 10
// 10
// 100
\end{lstlisting}

\verb|reference_wrapper|对象是可以存放入容器中的，普通的引用是不行的。所以\verb|reference_wrapper|对象可以通过容器来存放、管理大量对象的引用。

若\verb|reference_wrapper|包裹的引用是可以调用的，则\verb|reference_wrapper|对象也是可调用的。

\verb|std::ref| 和\verb|std::cref| 通常用来产生一个\verb|reference_wrapper|对象。

\verb|reference_wrapper| 常通过引用传递对象给\verb|std::bind|函数或者\verb|std::thread|构造函数。

\section{可变参数模板(variadic template)}

\subsection{基本概念}

可以创建一种模板函数和模板类，他们接受可变数量的参数

创建可变参数模板需要理解以下几个内容

\begin{enumerate}
\item 模板参数包(parameter pack)
\item 函数参数包
\item 展开参数包(unpack)
\item 递归
\end{enumerate}

元运算符(meta-operator)，即一个省略号(3个dot)

\begin{lstlisting}
template<typename... Args>
void show_list(Args... args)
{}
\end{lstlisting}

\paragraph{模板参数包和函数参数包}

在上面的代码示例中，\verb|Args|是一个模板参数包，而\verb|args|是一个函数参数包

\verb|T|只与一种类型匹配，而\verb|Args|可以与任意数量(包括零)类型匹配。

\verb|T|前面是\verb|typename|，而\verb|Arge|前面是\verb|typename...|

\paragraph{运用递归的思想展开参数包}

如何展开函数参数包呢？首先可知，不能使用\verb|[]|的方式。应该运用递归的思想，即，每次处理参数包中的第一个参数，将剩下的参数包再传递给递归调用，直至参数包中没有参数。

上面的模板写法，应当修改为

\begin{lstlisting}
template<typename T, typename... Args>
void show_list(T value, Args... args)
{
    std::cout << value << ", ";
    show_list(args...);
}
\end{lstlisting}

改进，如何处理最后一项，最后一项的后边不希望他是逗号，而希望是一个换行符。解决办法是，单独给出一个处理最后一项的模板，让他的行为与通用的模板稍有不同。

\begin{lstlisting}
template<typename T>
void show_list(T value)
{
    std::cout << value << std::endl;
}
\end{lstlisting}

除了按值展开参数包，也可以按引用展开参数包

可以将\verb|void show_list(T value, Args... args)|替换为

\begin{lstlisting}
void show_list(T value, const Args&... args)
\end{lstlisting}

\section{内联和嵌套命名空间}
C++11新增了内联命名空间，即声明命名空间的时候在前面加上关键字\verb|inline|

关键字\verb|inline|不能用在第一层的命名空间上，只能用在嵌套的命名空间上。

没有\verb|inline|关键字的命名空间，要想使用他内部的成员，必须显式地写完整命名空间。而如果想使用加了关键字\verb|inline|的嵌套命名空间里的成员，则不必写嵌套命名空间的名字。

\subsubsection{嵌套命名空间的简化语法}
\begin{lstlisting}
 namespace A{
     namespace B{
         namespace C{
             int foo(){};
         }
     }
 }   
\end{lstlisting}

可以写为

\begin{lstlisting}
namespace A::B::C{
    int foo(){};
}    
\end{lstlisting}

嵌套命名空间

\begin{lstlisting}
namespace A::B{
    inline namespace C{
        int foo(){};
    }
}    
\end{lstlisting}

可以写为

\begin{lstlisting}
namespace A::B::inline C{
    int foo(){};
}    
\end{lstlisting}

\section{正则表达式regex}
\subsection{认识正则表达式}
从C++11开始，C++开始支持正则表达式

正则表达式库为\verb|regex|，使用C++11及以上即可正常使用

\verb|#include <regex>|

正则表达式(regular expression)描述了一种字符串匹配的模式(pattern)，可以用来检查一个串是否含有某种子串、将匹配的子串替换或者从某个串中取出符合某个条件的子串等。

正则表达式，是一种程序读字符串的高级方式。普通的字符串搜索、识别，仅仅有逐个对比、查找完全相同的部分的这种简单的功能，而正则表达式，提供更复杂的"识别"方式，让程序可以更灵活的根据程序员的需要，来处理字符串。

\subsection{认识regex头文件}
\verb|regex|文件里总共有7个模板类以及若干实例化的类
\begin{table}[htbp]\centering
    \caption{regex里包含的类} \label{regex里包含的类}
    \begin{tabular}{|p{11em}|p{15em}|p{15em}|}
        \hline
        模板类&实例化类&功能\\ \hline
        \verb|basic_regex|&\verb|regex|：实际为\verb|basic_regex<char>|，\verb|wregex|：实际为\verb|basic_regex<wchar_t>|，下面的类似，不再一一详细说明&作为正则表达式对象，用于匹配文本\\ \hline
        \verb|match_results|&\verb|cmatch|，\verb|wcmatch|，\verb|smatch|，\verb|wsmatch|&用于获得匹配到的结果，实际可以看作\verb|sub_match|的数组\\ \hline
        \verb|sub_match|&\verb|csub_match|，\verb|wcsub_match|，\verb|ssub_match|，\verb|wssub_match|&保存捕获组，一般直接用\verb|match_result|数组访问的方式直接调用，所以一般看不到它\\ \hline
        \verb|regex_iterator|&\verb|cregex_iterator|，\verb|wcregex_iterator|，\verb|sregex_iterator|，\verb|wsregex_iterator|&用于遍历结果或子匹配的迭代器\\ \hline
        \verb|regex_token_iterator|&\verb|cregex_token_iterator|，\verb|wcregex_token_iterator|，\verb|sregex_token_iterator|，\verb|wsregex_token_iterator|&用于遍历未匹配部分的迭代器\\ \hline
        \verb|regex_error|&无&报告正则表达式库生成的错误\\ \hline
        \verb|regex_traits|&无&描述用于匹配的元素的特征。一般用不上，有需求的可参考官方文档\\ \hline
    \end{tabular}
\end{table}

特别需要注意的是，上面的类都是分类别配对使用的

\verb|regex ，cmatch，csub_match，cregex_iterator|

或者

\verb|regex ，smatch，ssub_match，sregex_iterator|

\subsubsection{std::smatch对象}
对\verb|std::smatch|对象使用\verb|str()|方法，可以传入一个\verb|int|值

\verb|smatch.str(0)|返回整个匹配的结果字符串，\verb|smatch.str(1)|返回字符串的第一个单元(第一个圆括号的字符串)

\subsection{文法}
\verb|regex|库默认使用ECMAScript文法

如果想要更改为其它文法，只需要在\verb|regex|构造函数中最后一位填入对应文法即可

\verb|regex r("<.*?>(.*)<.*?>", regex_constants::grep);|

当然除了选择文法，还可以选择其它标志，只需要将他们用符号 | 连接起来即可

如忽略大小写匹配可以写为

\verb\|regex r("<.*?>(.*)<.*?>", regex_constants::grep|regex_constants::icase);|\

还需要注意的是，C++中许多字符需要添加\ 符号进行转义才能使用，过于麻烦，所以C++11之后，出现了如下写法。使用该写法就可以不再转义即可使用，其中dem为任意字符，但要求前后一致即可，其它为固定写法

\verb|R"dem(内容)dem"|

\subsubsection{量词}
\begin{table}[htbp]\centering
    \caption{常用量词关键字} \label{常用量词关键字}
    \begin{tabular}{|c|l|}
        \hline 关键字 & 功能 \\ \hline
        \verb|*| & 前面的字符出现0次或多次 \\
        \verb|+| & 1次或多次\\
        \verb|?| & 0次或1次\\ 
        \verb|{n}| & n次\\ 
        \verb|{n,}| & n次到多次\\ 
        \verb|{n,m}| & n次到m次\\ 
        \hline
    \end{tabular}
\end{table}

\subsection{使用regex包}
\subsubsection{测试字符串}
该功能用到\verb|regex_match|函数

一般来说，最常用的就是下面这种写法：

比如测试用户输入字符串是否包含@符号：

\begin{verbatim}
regex r(".*@.*");
string str;
cin >> str;
if (regex_match(str, r)) {
    cout << "匹配成功";
}
else {
    cout << "匹配失败";
}
\end{verbatim}

\subsubsection{搜索子字符串}
该功能用到\verb|regex_search|函数

一般用法肯定是找出一段文本中所需要的子字符串，用法如下

该代码段就是不断从sStr中匹配符合r的子字符串，匹配成功则返回true，并将匹配到的结果放在m中，可通过\verb|m[n].str()|方式返回指定捕获组的子字符串

同时\verb|m[0].second|记录了当前匹配到的位置，所以通过它更新begin ，就可以遍历所有子字符串，直到无法匹配，返回false，结束

\begin{lstlisting}
string sStr; //要进行匹配的字符串
std::string::const_iterator begin = sStr.begin(); //开始迭代器
std::string::const_iterator end = sStr.end(); //结束迭代器
std::smatch m; //匹配的结果
regex r; //正则表达式
while (std::regex_search(begin, end, m, r)) {
    begin = m[0].second; //更新开始迭代器的位置
    m[n].str(); //获得第n个捕获组，其中0表示匹配到的全部子字符串
}
\end{lstlisting}

\subsubsection{使用迭代器}
如果你认为\verb|regex_search|用起来比较麻烦，则可以使用迭代器，用法如下

由于\verb|sregex_iterator|默认构造函数为指向最后一个元素之后,所以对end没有进行任何处理，只是作为一个结束标志

成员函数str可以返回指定捕获组的字符串，不传入数字则代表全部匹配内容

\begin{lstlisting}
regex r("-(.*?)-");
string s = "yushi-csdn--yushi-csdn";
sregex_iterator beg(s.begin(),s.end(),r);
sregex_iterator end;
for (; beg != end; beg++) {
    cout << beg->str(1) << endl;
}
\end{lstlisting}

\subsubsection{替换字符串}
该功能用到\verb|regex_replace|函数

该函数就是将sStr中匹配符合r的子字符串，将其全部替换为toReplace，并将结果返回到ret中

\begin{lstlisting}
string sStr; //要进行匹配的源字符串
regex r; //正则表达式
string toReplace; //进行替换的字符串
string ret=regex_replace(sStr, r, toReplace)
\end{lstlisting}

\section{时间运算 - std::chrono}
\verb|std::chrono|(\verb|#include <chrono>|)是一个用于处理与时间有关的数据的库，最早源于\verb|boost|，在C++11中添加入标准，并在后面的标准中持续更新。

\verb|std::chrono|主要以三种形式来处理时间，提供时钟计时的功能，表示时间点(time point)和表示时间段(duration)

\subsubsection{duration}
\verb|std::chrono::duration|用来表示一个时间段

类模板定义见代码示例\ref{std::chrono::duration类模板定义}

\begin{code_example}\label{std::chrono::duration类模板定义}
\begin{lstlisting}
// Defined in header <ratio>
// N表示分子，D表示分母
template <intmax_t N, intmax_t D = 1> class ratio;

template<
class Rep,  // 代表时钟tick的基本算数单位
class Period = std::ratio<1>  // 表示一个比值，"用秒表示的时间单位"
> class duration;
\end{lstlisting}
\end{code_example}

可以用于线程休眠的\verb|sleep_for()|函数，其函数原型为

\begin{lstlisting}
sleep_for(const chrono::duration<_Rep, _Period>& __rtime)
\end{lstlisting}

\paragraph{duration的简写方式}
使用\verb|duration|的时候，库提供了一些简写方式，见表\ref{std::chrono::duration Helper types}。注意，表中\verb|/* intXX */|的意思是，至少是XX位的有符号整型，这里没有写为固定值，这一点取决于编译器以及运行环境等外部条件。

\begin{table}[htbp]\centering
    \caption{std::chrono::duration的简写方式} \label{std::chrono::duration Helper types}
    \begin{tabular}{|l|l|}
        \hline Type & Definition \\ \hline
        \verb|std::chrono::microseconds| & \verb|std::chrono::duration</* int55 */, std::micro>| \\
        \verb|std::chrono::milliseconds| & \verb|std::chrono::duration</* int45 */, std::milli>| \\
        \verb|std::chrono::seconds| & \verb|std::chrono::duration</* int35 */>| \\
        \verb|std::chrono::minutes| & \verb|std::chrono::duration</* int29 */, std::ratio<60>>| \\
        \verb|std::chrono::hours| & \verb|std::chrono::duration</* int23 */, std::ratio<3600>>| \\
        \hline
    \end{tabular}
\end{table}

\paragraph{运算符重载}

提供了如下运算

\begin{itemize}
    \item 时间点 + 时间段 = 时间点
    \item 时间点 - 时间点 = 时间段
\end{itemize}

\paragraph{获取当前时间点}

见代码示例\ref{获取当前时间点}

\begin{code_example}\label{获取当前时间点}   
\begin{lstlisting}
auto t = std::chrono::steady_clock::now();
\end{lstlisting}
\end{code_example}

\paragraph{获取两个时间点之间的时间段}

\begin{code_example}
\begin{lstlisting}
auto dt = t1 - t0;
int64_t sec = chrono::duration_cast<chrono::seconds>(dt).count();
\end{lstlisting}
\end{code_example}

\paragraph{duration之间的转换}

\verb|std::chrono::duration_cast|将任意一个\verb|std::chrono::duration|对象转换为指定的\verb|ToDuration|类型

\begin{code_example}
\begin{lstlisting}
template< class ToDuration, class Rep, class Period >
constexpr ToDuration duration_cast( const std::chrono::duration<Rep,Period>& d );
\end{lstlisting}
\end{code_example}

可以看代码示例\ref{duration_cast用法}，展示了\verb|std::chrono::duration_cast|的一种用法

\begin{code_example}\label{duration_cast用法}   
\begin{lstlisting}
#include <chrono>
#include <iostream>
#include <ratio>
#include <thread>

void f()
{
    std::this_thread::sleep_for(std::chrono::seconds(1));
}

int main()
{
    const auto t1 = std::chrono::high_resolution_clock::now();
    f();
    const auto t2 = std::chrono::high_resolution_clock::now();
    
    // floating-point duration: no duration_cast needed
    const std::chrono::duration<double, std::milli> fp_ms = t2 - t1;
    
    // integral duration: requires duration_cast
    const auto int_ms = std::chrono::duration_cast<std::chrono::milliseconds>(t2 - t1);
    
    // converting integral duration to integral duration of
    // shorter divisible time unit: no duration_cast needed
    const std::chrono::duration<long, std::micro> int_usec = int_ms;
    
    std::cout << "f() took " << fp_ms << ", or "
    << int_ms << " (whole milliseconds), or "
    << int_usec << " (whole microseconds)\n";
}
\end{lstlisting}
\end{code_example}

代码输出

\begin{lstlisting}
f() took 1000.14ms, or 1000ms (whole milliseconds), or 1000000us (whole microseconds)
\end{lstlisting}

\section{其他}
C++11新增的其他功能

\begin{enumerate}
    \item 并行库thread等，关键字\verb|thread_local|
    \item 其他新增的库，比如random, chrono, tuple, ratio, regex
    \item 对底层编程支持的优化。放宽了对POD(plain old data)的要求。在C++98中，POD指的是标量类型或者没有构造函数的老式结构。以前的理念是，POD是可以安全地逐字节复制的东西。C++11保持了这个理念，同时扩展了POD的含义，让一些其他的数据也能按照POD的方式来处理。
    \item 允许共用体的成员由构造函数和析构函数，使得共用体的使用更加灵活。
    \item 解决了内存对齐的问题
    \item constexpr机制
    \item 字面量运算符(literal operator)
\end{enumerate}

\subsection{新的类型}
C++11新增了64位整型\verb|long long|和\verb|unsigned long long|

新增了\verb|char16_t|和\verb|char32_t|以支持16位和32位的字符。

\subsubsection{新的字面量后缀}
新的字面量后缀\verb|LL|和\verb|ULL|，小写也可以，比如\verb|long long x = 65536LL;|

\subsubsection{整型溢出问题}
\verb|std::numeric_limits|也给\verb|long long|类型适配了其上下限。

\subsubsection{新字符类型}
\verb|char16_t|和\verb|char32_t|，分别对应UTF-16和UTF-32。

\begin{lstlisting}
chat16_t utf16c = u’好’;
chat16_t utf16[] = u’hello world’;
\end{lstlisting}

\verb|wchar_t|，是Windows编程常用的字符类型

(since C++20)\verb|char8_t|，C++20引入的，对应UTF-8，前缀\verb|u8|，例如\verb|char str[] = u8”hello world”;|

(since C++20)在C++20之前，声明\verb|char|就能直接使用UTF-8，但是C++20之后，必须特别使用\verb|char8_t|才行。

\chapter{C++14新特性}
\section{(C++14)关键字deprecated}
关键字\verb|deprecated|是C++14新增的关键字。它可以用来标记有此属性的名字或实体被弃用，被弃用了以后你仍然可以使用，但是编译时会报warning。所以你在新版本库中弃用了某个函数，就可以使用deprecated告诉用户尽量不要使用。

\subsection{使用方法}
\begin{enumerate}
\item 弃用某个\verb|class/struct/union/enum|时，将关键字\verb|deprecated|放在\verb|class/struct/union/enum|和标识符之间，例如\verb|class deprecated className|

编译时会报\verb|warning: ‘className’ is deprecated|

\item 弃用typedef/using时，关键字deprecated的位置不一样

\verb|[[deprecated]] typedef wchar_t* WSTR;|

\verb|using STR [[deprecated]] = char*;|

\item 弃用变量(无论是全局变量还是类的成员变量)和函数时，关键字deprecated都是放在类型名的前面。

\item 弃用命名空间，关键字\verb|deprecated|放在\verb|namespace|和命名空间标识符的中间。

\item 弃用枚举量的成员，是放在该成员的后面，例如\verb|enum enumName{A, B, C deprecated};|

\end{enumerate}

\subsection{带字面量的关键字deprecated}
带字面量的关键字\verb|deprecated|，例如\verb|deprecated(“Do not use this”)|，编译时的\verb|warning|会打印出\verb|Do not use this|字符串

\section{std::integer\_sequence}
是C++14中引入的一个类模板，功能是产生一个整型数据序列(整数数列)。
\begin{lstlisting}
template< class T, T... Ints >
class integer_sequence;
\end{lstlisting}

第一个参数\verb|T|是类型参数，整型也分许多类型，比如\verb|unsigned int|,\verb|int|等

第二个参数\verb|T... Ints|是非类型参数的参数包。

有一个静态方法\verb|size()|，可以返回传入参数包的长度。
\begin{lstlisting}
static constexpr size_t size() noexcept {
    return sizeof...(Ints);
}
\end{lstlisting}

\subsection{std::make\_integer\_sequence}
针对 \verb|T| 为 \verb|std::size_t| 的常用情况，定义辅助别名模板 \verb|std::index_sequence| 。

\verb|std::make_integer_sequence|是一个别名模板(alias template)。

\verb|std::integer_sequence|需要传入具体的整型数据的值，而\verb|std::make_integer_sequence|只需要传入所需的个数，自动形成从0开始直至\verb|N - 1|的整型数列。
\begin{lstlisting}
template< class T, T N >
using make_integer_sequence = std::integer_sequence<T, /* a sequence 0, 1, 2, ..., N-1 */>;
\end{lstlisting}

使用举例
\begin{lstlisting}
std::make_integer_sequence<int, 20>{}
// output
// 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
\end{lstlisting}

\subsection{std::make\_index\_sequence}
\verb|std::make_index_sequence|是\verb|std::make_integer_sequence|的特殊形式(是他的一个别名模板)，将类型固定为\verb|std::size_t|
\begin{lstlisting}
template< std::size_t N >
using make_index_sequence = std::make_integer_sequence<std::size_t, N>;
\end{lstlisting}

使用举例
\begin{lstlisting}
std::make_index_sequence<10>{}
// output
// 0 1 2 3 4 5 6 7 8 9
\end{lstlisting}

\subsection{std::index\_sequence\_for}
\verb|std::index_sequence_for|可以为任何类型，生成一个相同长度的整型序列，或者成为索引序列，因为这个模板最常用的情况就是用于快速生成一个索引。
\begin{lstlisting}
template< class... T >
using index_sequence_for = std::make_index_sequence<sizeof...(T)>;
\end{lstlisting}

使用举例
\begin{lstlisting}
std::index_sequence_for<float, std::iostream, char>{}
// output
// 0 1 2
\end{lstlisting}

\section{std::exchange}
在头文件\verb|utility|中定义。

\verb|std::exchange|函数模板，他的功能是，用一个新值\verb|new_value|去替换旧值\verb|obj|，并返回旧值。
\begin{lstlisting}
template< class T, class U = T >
T exchange( T& obj, U&& new_value );
\end{lstlisting}

类型\verb|T|必须具有移动构造能力(MoveConstructible)

\subsubsection{应用场景 - 交换}
在C++14之前，如果你想交换两个变量的值，你可能需要一个临时变量来保存其中一个变量的值。但是使用\verb|std::exchange|就会更方便和简介
\begin{lstlisting}
int a = 5, b = 10;
a = std::exchange(b, a);
\end{lstlisting}

\subsubsection{应用场景 - 移动构造和移动赋值}
\verb|std::exchange| 的一个常见用途是在实现移动赋值运算符和移动构造函数时使用

在下面的代码示例中，移动构造函数和移动赋值运算符使用 \verb|std::exchange| 将 \verb|other.n| 的值移动到 \verb|n|，并在 \verb|other.n| 中留下零。这样可以确保 \verb|other.n| 在移动操作后处于有效状态。
\begin{lstlisting}
struct S
{
    int n;
    
    S(S&& other) noexcept : n{std::exchange(other.n, 0)} {}
    
    S& operator=(S&& other) noexcept
    {
        if (this != &other)
        n = std::exchange(other.n, 0); // move n, while leaving zero in other.n
        return *this;
    }
};

\end{lstlisting}

\section{std::quoted}
\verb|std::quoted|的功能是处理给字符串增加、删除双引号。

看一下cppreference上给出的代码示例
\begin{lstlisting}
#include <iomanip>
#include <iostream>
#include <sstream>

void default_delimiter()
{
    const std::string in = "std::quoted() quotes this string and embedded \"quotes\" too";
    std::stringstream ss;
    ss << std::quoted(in);
    std::string out;
    ss >> std::quoted(out);
    
    std::cout << "默认分隔符的情形:\n"
    "读取为 [" << in << "]\n"
    "存储为 [" << ss.str() << "]\n"
    "写出为 [" << out << "]\n\n";
}

void custom_delimiter()
{
    const char delim{'$'};
    const char escape{'%'};
    
    const std::string in = "std::quoted() quotes this string and embedded $quotes$ $too";
    std::stringstream ss;
    ss << std::quoted(in, delim, escape);
    std::string out;
    ss >> std::quoted(out, delim, escape);
    
    std::cout << "自定义分隔符的情形:\n"
    "读取为 [" << in << "]\n"
    "存储为 [" << ss.str() << "]\n"
    "写出为 [" << out << "]\n\n";
}

int main()
{
    default_delimiter();
    custom_delimiter();
}
\end{lstlisting}

程序输出为
\begin{verbatim}
默认分隔符的情形:
读取为 [std::quoted() quotes this string and embedded "quotes" too]
存储为 ["std::quoted() quotes this string and embedded \"quotes\" too"]
写出为 [std::quoted() quotes this string and embedded "quotes" too]

自定义分隔符的情形:
读取为 [std::quoted() quotes this string and embedded $quotes$ $too]
存储为 [$std::quoted() quotes this string and embedded %$quotes%$ %$too$]
写出为 [std::quoted() quotes this string and embedded $quotes$ $too]
\end{verbatim}

\section{拷贝省略(copy elision)}


\chapter{C++17新特性}
\section{写于其他章节的内容}
\subsubsection{std::optional}
详见第\ref{(C++17)std::optional}节
\subsubsection{std::variant}
详见第\ref{(C++17)std::variant}节

\section{(C++17)支持初始化语句的if和switch结构}
C++17开始，if和switch支持了初始化语句。

基本语法

\begin{lstlisting}
if (init; condition) {
    statements;
}
\end{lstlisting}

\verb|init|就代表初始化语句
\verb|init|中声明的变量，生命周期是\verb|if|的花括号内部以及之后的\verb|else if|和\verb|else|分支中，整个逻辑判断块全部结束后会自动消失(析构)。

\verb|else if|也有类似的结构，可以使用初始化语句，初始化语句里声明的变量的生命周期也可以顺延到后面的分支中，直至整个逻辑判断块结束。

\verb|switch|也支持\verb|(init; condition)|这样的结构，使用初始化语句。

\section{(C++17)结构化绑定(Structured Bindings)}
在结构化绑定出现之前，如果你想从一个元组中提取值，通常需要使用 \verb|std::get<N>(tuple)| 函数，比较繁琐。

\subsection{使用方法}
\subsubsection{遍历键值对}
在下面的代码示例中，\verb|myMap|中的每个元素的键和值，都绑定到变量\verb|key|和\verb|value|上。
\begin{lstlisting}
std::map<int, std::string> myMap = {{1, "one"}, {2, "two"}, {3, "three"}};
for (const auto& [key, value] : myMap) {
    std::cout << "Key: " << key << ", Value: " << value << std::endl;
}
\end{lstlisting}

\subsubsection{处理元组和结构体}
在过去，通过\verb|std::get<>|访问元组，需要记住成员的索引值index。

在下面的代码示例中，\verb|const auto [x, y, z] = myTuple;|，将\verb|myTuple|中的3个元素分别绑定到了x，y和z上。
\begin{lstlisting}
std::tuple<int, double, std::string> myTuple = {1, 2.2, "example"};
const auto [x, y, z] = myTuple;
std::cout << x << ", " << y << ", " << z << std::endl;
\end{lstlisting}

如果加上了引用，比如\verb|auto& [a, b, c] = value;|，那么是可以改动原始数据的。

\subsubsection{处理传统C数组}
\begin{lstlisting}
int a[3]{1, 3, 5};
auto[x, y, z] = a;
\end{lstlisting}

\subsection{结构化绑定现有的缺陷}
\begin{enumerate}
    \item 固定数量和已知类型的限制。对于具有可变数量元素的数据结构(如可变参数元组)，结构化绑定无法直接应用。这在处理泛型编程或模板元编程时尤其成为一个限制。
    \item 无法应用于动态数据结构。结构化绑定不适用于那些元素数量在运行时才确定的动态数据结构，例如标准库中的 \verb|std::vector| 或 \verb|std::list|。
    \item 作用域限制。结构化绑定创建的变量仅在声明它们的作用域内有效。这意味着绑定的变量不能在声明它们的那个特定作用域之外使用，这可能会限制某些编程模式的实现。
    \item 对复杂嵌套的局限性。在处理复杂嵌套的数据结构时，结构化绑定可能会变得笨拙。例如，如果一个元组包含另一个元组或复杂结构，直接应用结构化绑定可能会使代码变得难以理解和维护。
    \item 不支持自定义解包行为。结构化绑定不允许自定义解包行为。它仅仅是将数据结构中的元素直接绑定到变量，没有提供修改解包行为的方式。在需要自定义解包逻辑的场景中，这可能是一个限制。
    \item 编译器对错误的反馈可能不清晰。当使用结构化绑定时，特别是在模板编程中，编译器产生的错误信息可能不够清晰或直接。这可能会给调试和解决编译时问题带来一定的困难。
    \item 对老旧代码库的兼容性问题。结构化绑定是C++17的特性。
\end{enumerate}

\subsection{底层原理}
对于代码\verb|auto [x, y] = obj;|

首先，编译器创建一个与 \verb|obj| 类型匹配的匿名变量。

接着，编译器生成访问这个匿名变量的成员的代码。

然后，将访问到的成员，分别分配给 \verb|x| 和 \verb|y|。

\subsubsection{注意事项}
\begin{enumerate}
    \item 隐式引用： 结构化绑定的一个微妙之处在于，绑定到变量的元素可能是引用，这取决于绑定的表达式。这意味着修改绑定的变量可能会影响原始数据。
    \item 编译器依赖： 不同的编译器(如 GCC、Clang、MSVC)可能有不同的实现方式，但它们都遵循C++标准的规定。
    \item 特殊类型的处理： 对于自定义类型，结构化绑定的行为取决于特殊成员函数的存在(比如\verb|tuple_size|、\verb|tuple_element|、\verb|get|)
    \item 优化： 在实际使用中，编译器可能会应用优化来减少不必要的拷贝和临时变量的创建。
\end{enumerate}

\section{(C++17)inline说明符扩展了功能}
\subsection{用inline修饰变量，内联变量}
\verb|inline|用于修饰变量定义是在C++17之后的事情。当\verb|inline|用于修饰变量定义时，你很难说它具有内联展开的作用，因为将变量的定义内联展开到引用处没有意义，它更多地只是允许在多个编译单元对同一个变量进行定义，并且在链接时只保留其中的一份作为该符号的定义。

同时在多个源文件中定义同一个\verb|inline|变量必须保证它们的定义都相同，否则和\verb|inline|函数一样，你没办法保证链接器最终采用的是哪个定义。

\verb|inline|变量除了允许我们在头文件中定义全局变量，也允许我们在类定义中直接初始化静态数据成员。

使用\verb|inline|的静态变量，会被编译成弱符号，并存储在目标文件的\verb|.data|区。

C++17前只有内联函数，现在有了内联变量，我们印象中C++类的静态成员变量在头文件中是不能初始化的，但是有了内联变量，就可以达到此目的

\subsection{用inline修饰命名空间}
\verb|inline|命名空间最主要的用途是为程序库的版本控制提供语言上的支持，有点类似于共享库中的符号版本控制(将程序使用的接口版本信息记录到可执行文件，动态链接时再根据这些版本信息从共享库调用正确版本的接口)。

一般来说库的作者会为不同的版本放置到专属的命名空间，再用一个与版本无关的外围命名空间将它们包含，并通过预编译指令选择性地将开发环境支持的库版本对应的命名空间暴露给用户。

\subsubsection{优点}
在C++11之前，这种符号控制也可以使用\verb|using namespace|来实现，但是有如下缺点。
\begin{enumerate}
    \item 在内嵌命名空间声明的模板无法在外围命名空间中进行特化。一般来说库作者都不希望将版本相关的命名空间暴露给用户，而这个缺陷又要求用户必须在模板所在的命名空间中对其进行特化。
    \item 不支持ADL。
\end{enumerate}

inline与using的区别在于用inline修饰的内嵌命名空间的成员表现得更像声明在外围命名空间：你不仅可以在外围命名空间中对inline的内嵌命名空间的模板进行特化，而且ADL也会将参数类型所在命名空间所包含的inline命名空间，以及自身作为inline命名空间所在的外围命名空间一并考虑进来

\subsection{(C++20)新增特性}
\verb|consteval|函数，隐式地带有\verb|inline|特性。

整个定义都在 class/struct/union 的定义内的函数，若非被附着到具名模块，都是隐式的内联函数，无论它是成员函数还是非成员 friend 函数。A function defined entirely inside a class/struct/union definition, whether it's a member function or a non-member friend function, is implicitly an inline function unless it is attached to a named module(since C++20).

\section{字符串新工具std::string\_view}
\subsection{简介}
字符串视图\verb|std::string_view|，他并不直接拥有或者操作字符串对象，仅仅包含了对字符串对象的查看功能。他比\verb|std::string|性能要高，因为包含的功能更少。

巧妙使用\verb|std::string_view|可以避免字符串对象的潜在拷贝和创建过程，节省空间时间。

\paragraph{所占空间更小}
在一般的典型实现中，\verb|std::string_view|对象只有2个数据成员，指向某个字符串常量的指针，以及字符串长度。通常64位系统下大小为16个字节。

而\verb|std::string|对象，除了指针和长度，还有表示容量的\verb|size_type m_capacity|，8字节，和用于小对象存储优化的\verb|std::array<char, 16> m_sso|，16字节，加起来一共是40字节。

\paragraph{头文件}
在头文件\verb|<string_view>|中定义。

\subsection{使用}
\subsubsection{访问字符串内部}
支持基本的迭代器\verb|begin|，\verb|end|

支持方括号运算符\verb|operator[]|和\verb|at|

\paragraph{返回长度}
\verb|size()|和\verb|length()|都可以返回内部字符个数。

\verb|empty()|查看是否为空。

\subsubsection{避免UB}
看下面的代码示例
\begin{lstlisting}
const std::string& f(const std::string& str) {
    return str;
}
int main() {
    auto& ret = f("哈哈");
    std::cout << ret << '\n';
}
\end{lstlisting}

\verb|ret|接收了一个引用\verb|const std::string&|。这里产生了悬垂引用、悬空引用。

在函数调用中绑定到函数形参的临时量，存在到含这次函数调用的全表达式结尾为止：如果函数返回一个生命长于全表达式的引用，那么它会成为悬垂引用。

\subsection{新标准开始使用新接口}
新标准增加的库基本上不会再以\verb|const std::string&|这种作为形参，比如\verb|std::format|，\verb|std::vformat|。

\section{std::any}
\verb|std::any|是一种值类型，它能够更改其类型，同时仍然具有类型安全性。也就是说，对象可以保存任意类型的值，但是它们知道当前保存的值是哪种类型。在声明此类型的对象时，不需要指定可能的类型。

用于存储可拷贝构造的对象。

\subsubsection{成员函数}
\paragraph{修改删除与交换}
\verb|emplace|，更改所含对象，直接构造新对象

\verb|reset|，销毁所含对象

\verb|swap|，交换两个 \verb|std::any| 对象

\paragraph{查询}
\verb|has_value|，查询内部是否含有值

\verb|type|，返回所包含值的\verb|typeid|

\subsubsection{非成员函数}
\verb|std::swap(std::any)|，特化的\verb|swap|算法函数。

\verb|any_cast|，进行对所含有对象的类型安全访问。
\begin{lstlisting}
std::string s = std::any_cast<std::string>(a);
\end{lstlisting}

如果转换失败，因为对象为空或包含的类型不匹配，则抛出\verb|std::bad_any_cast|异常。

\verb|make_any|，创建\verb|std::any|对象

\chapter{C++20新特性}
\section{写于其他章节的内容}
\subsubsection{协程}
新关键字\verb|co_await|、\verb|co_yield| 和 \verb|co_return|

详见第\ref{(C++20/23)协程}节

\subsubsection{std::jthread}
详见第\ref{线程守卫 - 自动join，(C++20)jthread}节

\section{(未完成)新的数据类型char\_8t}

\section{consteval说明符}
\verb|consteval|说明符(consteval specifier)，用于将函数指定为立即函数(immediate function)，即每次调用该函数必须产生编译时常量。

立即函数，是\verb|constexpr|函数。

立即函数只能在编译时常量的上下文中使用。

与 \verb|constexpr| 一样，\verb|consteval| 说明符，从功能上，包含了 \verb|inline|的。不过，它不可应用于析构函数、分配函数(allocation functions)或解分配函数(deallocation functions)。

\subsubsection{代码示例}
来自cppreference上面的代码示例

直接给\verb|consteval|函数\verb|sqr()|传入字面值\verb|100|是可以的。

传入编译时常量\verb|x|也是可以的，但是函数返回给\verb|r2|是不可以的，因为变量\verb|r2|并没有\verb|constexpr|说明符。

\verb|consteval|函数可以在另一个\verb|consteval|函数中调用，虽然形式上不是常量表达式，但是允许。
\begin{lstlisting}
consteval int sqr(int n)
{
    return n * n;
}
constexpr int r = sqr(100); // OK

int x = 100;
int r2 = sqr(x);            // 错误：调用未能产生常量

consteval int sqrsqr(int n)
{
    return sqr(sqr(n));     // 在此处不是常量表达式，但是 OK
}

constexpr int dblsqr(int n)
{
    return 2 * sqr(n);      // 错误：外围函数并非 consteval 且 sqr(n) 不是常量
}
\end{lstlisting}

\section{constinit说明符}
\verb|constinit| 说明符声明拥有静态或线程存储期的变量。

如果变量以 \verb|constinit| 声明，那么它的初始化声明必须应用 \verb|constinit|。

可以用于防止未初始化的静态变量带来的问题，防止存在某个变量未在编译期完成初始化。

\section{大小端类型std::endian}
定义于头文件 \verb|<bit>|

\begin{lstlisting}
enum class endian
{
    little = /*implementation-defined*/,
    big    = /*implementation-defined*/,
    native = /*implementation-defined*/
};
\end{lstlisting}

如果机器是小端存储，则\verb|std::endian::native == std::endian::little|

否则\verb|std::endian::native == std::endian::big|。

\begin{lstlisting}
#include <bit>
#include <iostream>

int main()
{
    if constexpr (std::endian::native == std::endian::big)
        std::cout << "big-endian\n";
    else if constexpr (std::endian::native == std::endian::little)
        std::cout << "little-endian\n";
    else
        std::cout << "mixed-endian\n";
}
\end{lstlisting}

\section{位域运算std::rotl和std::rotr}
\verb|std::rotl|执行循环左移运算，\verb|std::rotr|执行循环右移运算。

函数原型

\begin{lstlisting}
template< class T >
[[nodiscard]] constexpr T rotl( T x, int s ) noexcept;
\end{lstlisting}

\begin{itemize}
    \item \verb|T x|，需要进行运算的数，
    \item \verb|int s|，左移的位数。
\end{itemize}

\begin{code_example}\label{}
\begin{lstlisting}
#include <bit>
#include <bitset>
#include <cstdint>
#include <iostream>

int main()
{
    const std::uint8_t i = 0b00011101;
    std::cout << "i          = " << std::bitset<8>(i) << '\n';
    std::cout << "rotl(i,0)  = " << std::bitset<8>(std::rotl(i, 0)) << '\n';
    std::cout << "rotl(i,1)  = " << std::bitset<8>(std::rotl(i, 1)) << '\n';
    std::cout << "rotl(i,4)  = " << std::bitset<8>(std::rotl(i, 4)) << '\n';
    std::cout << "rotl(i,9)  = " << std::bitset<8>(std::rotl(i, 9)) << '\n';
    std::cout << "rotl(i,-1) = " << std::bitset<8>(std::rotl(i, -1)) << '\n';
}
\end{lstlisting}
\end{code_example}

程序输出
\begin{lstlisting}
i          = 00011101
rotl(i,0)  = 00011101
rotl(i,1)  = 00111010
rotl(i,4)  = 11010001
rotl(i,9)  = 00111010
rotl(i,-1) = 10001110    
\end{lstlisting}

\subsection{(C++20)std::bit\_cast}
用于进行不同类型之间的位级别的转换。

在C++中，类型别名转换通常使用\verb|reinterpret_cast|进行，但它是非常危险的，可能会引起未定义行为。而\verb|std::bit_cast|的出现就是为了避免这些问题，并提供了更可靠的位级别类型转换。

\begin{lstlisting}
template< class To, class From >
constexpr To bit_cast( const From& from ) noexcept;
\end{lstlisting}

参数含义

\begin{itemize}
    \item \verb|class To|要转换的目标类型。
    \item \verb|class From|，被执行转换的源类型。
\end{itemize}

在下面的代码示例中，\verb|decltype(&fun)|用来获取\verb|fun|函数类型。通过\verb|bit_cast|转换后\verb|x|和\verb|fun|的地址一致。\verb|kk|是\verb|fun|函数定义的类型，使用\verb|kk()|就是调用\verb|fun|函数。

\begin{code_example}\label{}
\begin{lstlisting}
#include <stdio.h>
#include <iostream>
#include "bit_array.hpp"
void fun() {
    std::cout << "I am fun." << std::endl;
}
int main() {
    long x = bit_cast<long, decltype(&fun)>(fun);
    printf("fun address:0x%0x\n", fun);
    printf("fun address with bit cast:0x%0x\n", x);
    
    decltype(&fun) kk;
    kk = fun;
    kk();   // call fun
    
    return 0;
}    
\end{lstlisting}
\end{code_example}

\section{位域默认初始化}\label{(C++20)位域默认初始化}
C++20允许对数据成员的位域进行默认初始化。

在下面的代码示例中，低 8 位被初始化为 11，高 4 位被初始化为 7
\begin{lstlisting}
class S {
public:
    int x : 8 = 11;
    int y : 4{7};
};    
\end{lstlisting}

\section{指定初始化(designated initializers)}
举例，看下面的代码示例。他的功能是，初始化了结构体A中的成员x和z，而y没有明确给出值来初始化，所以初始化为0。这项技术称为\textbf{指定初始化(designated initializers)}，或\textbf{指定初始值设定项}。
|
\begin{lstlisting}
struct A {
    int x;
    int y;
    int z = 123;
};

A a {.x = 1, .z = 2}; // a.x == 1, a.y == 0, a.z == 2    
\end{lstlisting}

这个特性不是什么新知识，C语言早在C99中就已经存在了。在C99中，甚至允许这样的方式来初始化数组

\verb|int arr[3] = { [1] = 1, [2] = 2 };|

\subsubsection{聚合体类型}
C++20中对Designated initializers目前只能作用于聚合体类型(Aggregate)。聚合体类型的详细内容，请参见第\ref{聚合类(aggregate class)}节

\subsubsection{规则}
\begin{enumerate}
    \item designated initializers 必须是按照成员的声明顺序进行初始化的
    \item designated initializers 仅仅用于初始化直接非静态数据成员
    \item designated initializers 只能用于 aggregate 初始化
    \item 并不需要所有的成员出现在initializer list中
    \item 不能在初始化表达式中同时使用designated initializers和non-designated initializers
    \item 成员的designated initializers必须在initializer list中出现一次
    \item designated initializers不能够嵌套
\end{enumerate}

\subsection{应用举例}
\subsubsection{函数返回值}
\begin{lstlisting}
#include <iostream>

class Point {
    public:
    int x_;
    int y_;
};

Point getPoint() {  // 增强了可读性
    return Point{
        .x_ = 1,
        .y_ = 2
    };
}

int main() {
    const auto& p = getPoint();
    std::cout << p.x_ << " " << p.y_ << "\n";
    return 0; 
}    
\end{lstlisting}

\subsubsection{重载决议}
\begin{code_example}\label{}
\begin{lstlisting}
#include <iostream>
#include <source_location>

struct Point {
    /* data */
    int a_;
    int b_;
};

struct Point3 {
    int a_;
    int b_;
    int c_;
};

void func(Point p) {
    const std::source_location location =
    std::source_location::current();
    std::cout << location.function_name() << "\n";
    
}

void func(Point3 p) {
    const std::source_location location =
    std::source_location::current();
    std::cout << location.function_name() << "\n";
    
}

int main() {
    func({.a_ = 1});
    // func({.a_ = 2, .b_ = 3, .c_ = 4});
    return 0;
}
\end{lstlisting}
\end{code_example}

\verb|func({.a_ = 1});|在这句语句中，编译器会报错。
\begin{lstlisting}
error: call of overloaded 'func(<brace-enclosed initializer list>)' is ambiguous
31 |     func({.a_ = 1});
|     ~~~~^~~~~~~~~~~    
\end{lstlisting}

其原因是，由于\verb|struct Point|和\verb|struct Point3|都有名为\verb|a_|的成员，所以这里产生了二义性。

\section{模板元编程 - concept与requires}\label{模板元编程 - concept与requires}
\subsection{欲解决的问题}
\verb|concept|是用来约束模板类型的语法糖。原来，模板通过SIFNAE机制进行模板的约束匹配，这样的匹配方式，我知道的缺陷有几个

\begin{enumerate}
    \item 匹配失败时报错信息难以阅读
    \item 匹配的逻辑写的实在是难以读懂，跟模板本身逻辑耦合在一起。
    \item 不同地方的匹配可能需要写很多相同模式的约束匹配的代码。
\end{enumerate}

\verb|concept|的提出就是为了解决上述问题，它通过将模板的类型约束抽象出来，然后在模板定义时再使用。这样成功解耦了模板类型约束和模板本身的一些类型逻辑。

\subsubsection{具体实例}
以一个简单的加法实现为例，说明\verb|concept|和\verb|requires|是如何运作的。

\begin{lstlisting}
#include <iostream>
​
struct Foo {};
​
template <typename T>
T Add(T a, T b) {
    return a + b;
}
​
int main() {
    std::cout << Add(1, 2) << std::endl;
    Foo f1, f2;
    std::cout << Add(f1, f2) << std::endl;
    return 0;
}
\end{lstlisting}

从上面的代码示例可以看出，在\verb|Add(f1, f2)|这一句是一定会报错的。但是报出来的错误，可能是比如未定义重载运算符\verb|operator<<|和\verb|operator+|等等。但是，并未给出最直接的一个信息，即，结构体\verb|f1|和\verb|f2|并不能执行相加运算。

然后我们引入\verb|concept|和\verb|requires|，如下代码示例所示

\begin{lstlisting}
template <typename T>
concept Addable = requires(T a, T b) { a + b; };
​
template <typename T>
requires Addable<T>
T Add(T a, T b) {
    return a + b;
}    
\end{lstlisting}

这样，我们可能会得到更人性化、更直接的报错信息。如下所示。

\begin{lstlisting}
add_concept.cc: In function 'int main()':
add_concept.cc:17:19: error: no matching function for call to 'Add(Foo&, Foo&)'
        17 |   std::cout << Add(f1, f2) << std::endl;
           |                ~~~^~~~~~~~
add_concept.cc:10:3: note: candidate: 'template<class T>  requires  Addable<T> T Add(T, T)'
        10 | T Add(T a, T b) {
           |   ^~~
add_concept.cc:10:3: note:   template argument deduction/substitution failed:
add_concept.cc:10:3: note: constraints not satisfied
add_concept.cc: In substitution of 'template<class T>  requires  Addable<T> T Add(T, T) [with T = Foo]':
add_concept.cc:17:19:   required from here
add_concept.cc:6:9:   required for the satisfaction of 'Addable<T>' [with T = Foo]
add_concept.cc:6:19:   in requirements with 'T a', 'T b' [with T = Foo]
add_concept.cc:6:42: note: the required expression '(a + b)' is invalid
         6 | concept Addable = requires(T a, T b) { a + b; };
           |                                        ~~^~~
\end{lstlisting}

\subsection{concept}
头文件与基本的使用方式

概念名(concept-name)，约束表达式(constraint-expression)

\begin{lstlisting}
#include <concepts>
    
template <template-parameter-list>
concept concept-name = constraint-expression;
\end{lstlisting}

\subsubsection{约束表达式(constraint-expression)}
约束表达式是一个可以被eval为bool的表达式或者编译期函数。

在使用定义好的\verb|concept|时，约束表达式会根据上面模板参数列表(template-parameter-list)传入的类型，执行编译期计算，判断使用该\verb|concept|的模板定义是否满足。

如果不满足，则编译期会给定一个具有明确语义的错误，即，这个\verb|concept|没有匹配成功等带有明确含义的报错信息。 

上文所述的匹配行为，是在编译期完成的，因此\verb|concept|其实是zero-cost的。

\begin{lstlisting}
// 一个永远都能匹配成功的concept
template <typename T>
concept always_satisfied = true; 

// 一个约束T只能是整数类型的concept，整数类型包括 char, unsigned char, short, ushort, int, unsinged int, long等。
template <typename T>
concept integral = std::is_integral_v<T>;

// 一个约束T只能是整数类型，并且是有符号的concept
template <typename T>
concept signed_integral = integral<T> && std::is_signed_v<T>;
\end{lstlisting}

\subsubsection{编译器支持}
需要GCC(10.0+)，Clang(10.0+)，编译选项：\verb|-std=c++20/-std=c++2a|

\subsubsection{约束模板参数, 替换typename}
我们给模板传入的模板参数，他带有约束条件了，也就是之前通过约束表达式(constraint-expression)告诉\verb|concept|的。

编译时，必须满足这样限制的模板参数，才能通过编译，否则，编译期间就会报错了，而且报错的内容也是便于阅读的信息了。

\begin{lstlisting}
// template <typename T>  typename->Addable
template <Addable T>
T Add(T a, T b) {
    return a + b;
}
\end{lstlisting}

\subsubsection{如何理解concept}
\verb|concept|其实是一个语法糖，它的本质可以认为是一个模板类型的\verb|bool|变量。

定义一个\verb|concept|本质上是在定义一个\verb|bool|类型的编译期的变量。

使用一个\verb|concept|本质上是利用SFINAE机制来约束模板类型。但是使用\verb|concept|明显更友好、更便捷了，不需要精心研究SFINAE等精巧设计了。

\subsection{requires关键字}
\verb|requires|除了和\verb|concept|一起使用之外，还可以单独使用。

\subsubsection{requires条件}
使用已定义的\verb|concept|

\begin{lstlisting}
// 这里的Addable是一个定义好了的concept
template <typename T> requires Addable<T>
T Add(T a, T b) { ... }
\end{lstlisting}

\subsubsection{requires表达式}
在\verb|concept|的时候同时使用\verb|requires|

\begin{lstlisting}
template <typename T>
concept Addable = requires(T a, T b) { a + b; };
\end{lstlisting}

\subsubsection{放在类模板中}
除了在模板中，在类模板中也可以使用\verb|requires|

\begin{lstlisting}
// 这里的Addable是一个定义好了的concept
template <typename T> requires Addable<T> 
class Bar {
    public:
    T Add(T a, T b) { return a + b; }
};    
\end{lstlisting}

\subsubsection{函数尾随requires子句}

并没有用\verb|Addable|直接替代\verb|typename|，而是将之放在了函数特征标的后面。功能一样的，只是编译器灵活地支持了一种额外的写法。

\begin{lstlisting}
template <typename T>
T Add(T a, T b) requires Addable<T>
{
    return a + b;
}
\end{lstlisting}

\section{(未完成)模块modules}

\section{(未完成)范围库ranges}

\section{(未完成)格式化库std::format}

\section{三路比较}
至少需要 2 次比较才能确定结果是小于、大于和等于中的哪一种，这在比较的性能开销较大时会造成不必要的性能消耗；

为自定义类型定义比较运算时，需要定义 6 次，而这其中大部分是重复工作。

为了以统一的方式解决第一个问题，C++20 引入了三路比较运算符 <=>(又称宇宙飞船运算符 (Spaceship Operator)，得名于它长得很像一艘宇宙飞船)

偏序关系\verb|std::partial_ordering|

弱序关系\verb|std::weak_ordering|

强序关系\verb|std::strong_ordering|

\section{std::span}
C++20引入了\verb|std::span|作为一种语法糖，用于表示连续内存范围。它提供了一种轻量级的、非拥有式的、零开销的方式来引用数组或其他连续内存块。

头文件\verb|<span>|

\verb|std::span|可以用于传递数组片段给函数，或者在函数内部对连续内存进行操作，而无需进行内存拷贝。

可以把一个数组的整体(或者其一部分)，传递给模板span，然后就可以用一些通用的容器方法来操作这个数组了，而不必再已经有一个数组的基础上再去创建一个新的容器。

\verb|std::span|仅仅是引用连续内存范围的一种方式，它并不拥有这块内存。因此，在使用\verb|std::span|时需要确保被引用的内存块的生命周期大于或等于\verb|std::span|对象的生命周期，以避免悬空引用。

代码示例\ref{std::span}展示了\verb|std::span|的使用方式。

定义了一个\verb|printNumbers|函数，它接受一个\verb|std::span<int>|类型的参数\verb|numbers|，并打印其中的数字。在\verb|main|函数中，我们创建了一个包含5个元素的整数数组\verb|arr|，然后使用\verb|std::span<int>|来引用数组的前3个元素，即从\verb|arr[0]|到\verb|arr[2]|。我们将这个\verb|std::span|对象传递给\verb|printNumbers|函数进行打印。
\begin{code_example}
\label{std::span}
\begin{lstlisting}
#include <iostream>
#include <span>

void printNumbers(std::span<int> numbers) {
    for (int number : numbers) {
        std::cout << number << " ";
    }
    std::cout << std::endl;
}

int main() {
    int arr[] = {1, 2, 3, 4, 5};
    std::span<int> numbers(arr, 3); // 使用数组arr的前3个元素创建span
    
    printNumbers(numbers);
    
    return 0;
}
\end{lstlisting}
\end{code_example}

\subsubsection{构造函数}
\verb|std::span|的构造函数接受两个参数：指向连续内存的指针和元素的数量。我们可以使用指向数组的指针和数组的大小来创建一个\verb|std::span|对象，也可以使用迭代器范围或容器来创建。

\verb|std::span|提供了一些有用的成员函数和操作符，例如\verb|size()|返回元素的数量，\verb|operator[]|用于访问指定位置的元素，以及\verb|begin()|和\verb|end()|返回迭代器，允许使用范围循环和算法操作。

需要注意的是，\verb|std::span|仅仅是引用连续内存范围的一种方式，它并不拥有这块内存。因此，在使用\verb|std::span|时需要确保被引用的内存块的生命周期大于或等于\verb|std::span|对象的生命周期，以避免悬空引用。

\subsubsection{返回子集subspan()}
成员函数\verb|std::span<T,Extent>::subspan|可以获取一个\verb|std::span|的子集，第一个参数是偏移量offset，第二个参数是个数count，子集就是从偏移量处开始计数制定个数的元素，作为子集，返回这个\verb|std::span|

\begin{lstlisting}
constexpr std::span<element_type, std::dynamic_extent>
subspan( size_type Offset, size_type Count = std::dynamic_extent ) const;
\end{lstlisting}

\section{对智能指针的扩展}
C++20增加了对\verb|shared_ptr|进行原子操作的支持。

对\verb|make_unique|和\verb|make_shared|进行了扩展，支持创建具有非公共构造函数的对象，这是通过引入了对\verb|std::allocator|的支持来实现的。

\section{日期与时间库chrono的扩展}

\part{C++并发编程}
\chapter{C++并发开发}
\section{进程与线程}
\subsection{基本概念}
\subsubsection{进程与线程}
进程(process)，是一个正在执行中的程序，每一个进程执行都有一个执行顺序，该顺序是一个执行路径，或者叫一个控制单元

线程(thread)，是一个进程内的单一连续的控制流

我们在编写一个最简单的C++程序时，其主函数\verb|main()|对应的就是一个线程，最后编译生成的二进制文件，启动这个二进制文件，就是在操作系统中启动了一个进程。

由于这样简单的程序，里面只有一个函数\verb|main()|函数，因此，只有一个线程，这样的线程又可以称之为主线程。

因此我们给出一个阶段性结论，一个进程至少会有一个线程，即主线程。主线程结束，也就是\verb|main()|函数结束，函数返回，则主进程也随之结束，因为这个进程已经没有其他线程正在运行了。

\subsubsection{进程模型}
进程模型，是涉及操作系统领域的一个基本概念，主要论述了如下的思想。

进程是操作系统对于正在运行的程序的一个抽象。一台带有操作系统的计算机，可以运行许多个程序，在操作系统的视角下，他们每一个单独的程序，都以进程的形式而存在并进行管理。

如果计算机需要执行多个任务(多个进程)，如果单纯是等待一个进程结束后再运行下一个进程，那么满足不了大部分常用工况的。大部分应用场景下，计算机都需要处理许多项工作。(如果一次只处理一项工作，那么更有可能是，选择架构更简单的，甚至没有操作系统的计算机，或者说选用单片机即可)

CPU在进行运算时，其实际行为是，从代码队列中取出代码，执行，并将结果存放到指定位置，然后再取下一条代码(这是计算机基本原理的知识点)。

\subsubsection{上下文切换}
每一个程序，向下查看到底层层面，都是拆解成"有限但是数量众多的"前面叙述的这种简单基础运算过程的。

基于这一点，过去的程序员，设计出一种"上下文切换"的理念，让一个CPU可以处理多个任务。

CPU在执行A任务时，执行完一定的基础运算过程后，将整个CPU内相关的寄存器数值保存到一个缓存中，然后读取任务B的命令队列，开始对任务B执行基础运算过程。执行一定次数的运算后，再次重复之前保存寄存器数值的过程，然后把之前保存的A任务的寄存器数值，都提取出来，还原到了刚才执行A任务结束之前的状态，然后就可以继续执行A任务。再执行一定次数的运算之后，再次重复这样的过程。一个CPU就实现了在任务A和任务B之间快速切换的功能。

易知，CPU寄存器的读写速度是非常快速的。也就是说，这种切换过程，可以发生在极短时间内。

由于采用了这种"上下文切换"的设计理念，在肉眼中，计算机仿佛具有了同时处理多项工作的能力。(截止目前都是对过去单核CPU计算机而言的，目前的CPU功能更强大。而且进程模型也并非本段描述的这么简单，还涉及了很多问题，详情请查阅操作系统领域知识)

\subsubsection{时间开销}
虽然CPU在做简单的读写时，运算速度是非常高的，但是积累起来仍然会带来延迟。所以在进行并发开发时，必须要注意时间开销的问题。不是线程越多，程序运行速度越快。

\subsection{进程与线程的区别}
\begin{itemize}
    \item 进程是资源(包括内存、打开的文件等)分配的单位，线程是 CPU 调度的单位
    \item 进程拥有一个完整的资源平台，而线程只独享必不可少的资源，如寄存器和栈
    \item 线程同样具有就绪、阻塞、执行三种基本状态，同样具有状态之间的转换关系
    \item 线程能减少并发执行的时间和空间开销
    \begin{enumerate}
    	\item 线程的创建时间比进程快，因为进程在创建的过程中，还需要资源管理信息，比如内存管理信息、文件管理信息，而线程在创建的过程中，不会涉及这些资源管理信息，而是共享它们
    	\item 线程的终止时间比进程快，因为线程释放的资源相比进程少很多
    	\item 同一个进程内的线程切换比进程切换快，因为线程具有相同的地址空间(虚拟内存共享)，这意味着同一个进程的线程都具有同一个页表，那么在切换的时候不需要切换页表。而对于进程之间的切换，切换的时候要把页表给切换掉，而页表的切换过程开销是比较大的；
    	\item 由于同一进程的各线程间共享内存和文件资源，那么在线程之间数据传递的时候，就不需要经过内核了，这就使得线程之间的数据交互效率更高了
    \end{enumerate}
\end{itemize}

一个进程可以有多个并发执行、同时执行的线程。由于这些线程运行在同一个进程内，所以他们分享内存和其他资源。编写多线程处理程序中主要的困难是在不同线程之间协调对这些资源的应用。

使用线程，可以创建一个更加松散耦合的程序架构。

\subsection{并发机制}
\subsubsection{如何实现并发}
\begin{itemize}
    \item 使用多个单线程进程
    \item 使用一个(或多个)多线程进程
\end{itemize}

\subsubsection{进程间通信方式}
\begin{itemize}
    \item 同一电脑中：管道，文件，消息队列，共享内存
    \item 不同电脑间：socket通信技术
\end{itemize}

\subsubsection{线程间通信方式}
\begin{itemize}
    \item 共享内存(全局变量，全局指针等)
\end{itemize}

注意数据一致性问题，"同时读同时写"的时候

\subsubsection{使用C++进行并发开发}
windows自己具有他的线程API，而linux有POSIX规范(Portable Operating System Interface of UNIX)的线程开发技术。

POSIX thread是 操作系统级(OS level)的API规范，主要用来定义线程及线程间同步的相关操作，采用C语言定义。POSIX规范主要在unix like类系统上实现

C++最初是没有并发机制的。因为C++来自于C，而早期的C是没有的并发的，所以C++刚开始也没有，当然也包含了其他原因。

\paragraph{C++11新增了支持并发的库}
从C++11开始，C++语言将并发机制开发技术，集成到了语言标准之中，提供了以下几个库用于并发机制编程。
\begin{lstlisting}
<thread> <mutex> <atomic> <condition_variable> <future>
\end{lstlisting}

C++20开始增加了信号量机制\verb|<semaphore>|

这样就免去了，各个C++程序在开发时，使用开源但分散的并发支持技术，从而造成难以互通的情况发生。

从此，只要使用C++语言，如果要进行并发开发，那么就会倾向于使用C++标准中给出的统一的库来开展，极大提高了互通性和规范性。

\paragraph{pthread和thread之间的关系}
pthread早于thread出现，本来是在类POSIX系统中用来多线程编程的，Windows原生不支持。C++11之后， 只要Windows、Linux支持C++11都可以使用原生的thread头文件, 这是C++标准委员会支持的原生语法。

尽管thread可以用来进行跨平台的多线程编程，但是thread跟pthread有诸多不同。

thread是C++的API, 不可以在C++中调用，换句话说，它更加简单和安全。 它大量使用RAII来确保资源在超出范围时得到回收，并允许您从函数对象创建一个线程，具有所有的灵活性，而不是被限制为C风格的自由函数。

pthread是一个C的API，因此它不提供任何RAII，这使得它更难使用，更容易出错，特别是就异常安全性而言(除非你自己编写了包装，你现在必须调试 并保持)。所谓RAII是Resource Acquisition Is Initialization的简称，是C++语言的一种管理资源、避免泄漏的惯用法，利用的是C++构造的对象最终会被销毁的原则。

\section{thread库}
\subsection{线程对象 - std::thread}
声明一个\verb|std::thread|类对象，并在初始化时即用圆括号传给他一个函数名。

这样，一个运行指定函数的线程即创建完成了。这个函数在返回时，线程即会结束。

有下面的代码示例

\begin{lstlisting}
#include <thread>
#include <iostream>

// 持续打印字符串，间隔1600 ms
void printTextEndlessly()
{
    while ( 1 )
    {
        std::cout << "print text." << std::endl;
        std::this_thread::sleep_for(std::chrono::milliseconds(1600));
    }
}

// 持续打印数字且逐次+1，间隔1000 ms
void printNumberEndlessly()
{
    int i = 0;
    while ( 1 )
    {
        std::cout << "i = " << i << std::endl;
        i++;
        std::this_thread::sleep_for(std::chrono::milliseconds(1000));
    }
}

int main()
{
    // 新建一个线程来运行printNumberEndlessly()函数
    std::thread thrd_test(printNumberEndlessly);
    // 主线程中运行printTextEndlessly()函数
    printTextEndlessly();
    
    return 0;
}
\end{lstlisting}

程序输出结果

可以看到，两个函数同时开始运行，两个都是以某一时间间隔向终端上打印信息的函数，二者时间间隔不同，所以终端上的并非以相同的交替频次进行。
\begin{lstlisting}
print text.
i = 0
i = 1
print text.
i = 2
i = 3
print text.
i = 4
print text.
i = 5
i = 6
print text.

...
\end{lstlisting}
\subsubsection{std::thread接收一个可调用对象}
\verb|std::thread|类对象接收一个可调用对象来创建线程。

什么是可调用对象，这是C++里面的一个概念，函数，函数指针，lambda表达式，bind对象，重载运算符。

\paragraph{join()}
\verb|join()|的功能是，阻塞当前线程(在上面的代码示例中，就是\verb|main()|函数)，直到调用这一成员函数的线程对象的线程执行结束之后，再与当前线程一齐继续执行。

\verb|join()|之后的语句，在线程结束之前，是不会执行的。

\begin{lstlisting}
#include <thread>
#include <iostream>

// 打印10次字符串，间隔1600 ms
void printTextFor10Times()
{
    int i = 0;
    while ( i < 10 )
    {
        std::cout << "print text. i = " << i << std::endl;
        std::this_thread::sleep_for(std::chrono::milliseconds(500));
        i++;
    }
    std::cout << "printTextFor10Times() function finished! " << std::endl;
    return;
}

// 打印5次数字且逐次+1，间隔1000 ms
void printNumberEndlessly()
{
    int j = 0;
    while ( j < 5 )
    {
        
        std::cout << "j = " << j << std::endl;
        j++;
        std::this_thread::sleep_for(std::chrono::milliseconds(200));
    }
    std::cout << "printNumberEndlessly() function finished! " << std::endl;
    return;
}

int main()
{
    // 新建一个线程来运行printTextFor10Times()函数
    std::thread thrd_test(printTextFor10Times);
    // 先让主线程暂停在这里，等待thrd_test运行结束
    thrd_test.join();
    printNumberEndlessly();
    
    return 0;
}
\end{lstlisting}

程序输出结果
\begin{lstlisting}
print text. i = 0
print text. i = 1
print text. i = 2
print text. i = 3
print text. i = 4
print text. i = 5
print text. i = 6
print text. i = 7
print text. i = 8
print text. i = 9
printTextFor10Times() function finished! 
j = 0
j = 1
j = 2
j = 3
j = 4
printNumberEndlessly() function finished!
\end{lstlisting}

\paragraph{detach()}
主线程与子线程实现分离，子线程会到后台执行。由C++运行时库接管此线程，负责其后续的执行以及释放资源等工作。

一旦使用了\verb|detach()|，子线程就无法再受主线程控制了，就无法再\verb|join()|了

\paragraph{joinable()}
返回一个布尔值，表示该线程是否可以进行\verb|join()|或者\verb|detach()|

\paragraph{native\_handle()}
返回底层的实现定义的线程句柄。

其类型由具体的平台决定。在POSIX兼容系统上，它通常是\verb|pthread_t|类型；在Windows上，则是\verb|HANDLE|类型。这个句柄可以用于执行一些非标准的操作，比如直接与操作系统的线程API进行交互。

返回值和使用方式高度依赖于特定的操作系统，这降低了代码的可移植性。在使用之前，需要对目标平台有深入的了解。

\paragraph{get\_id()}
获取线程ID，返回一个类型为\verb|std::thread::id|的对象

\verb|std::thread::id|表示线程ID，定义了在运行时操作系统内唯一能够标识该线程的标识符，同时其值还能指示所标识的线程的状态。

\paragraph{hardware\_concurrency()}
\verb|thread.hardware_concurrency()|获取硬件支持的并发数

\paragraph{swap()}
交换2个\verb|thread|对象。调用后，可通过\verb|get_id()|查看交换的结果。
\begin{lstlisting}
void swap( std::thread& other ) noexcept;  // (since C++11)
\end{lstlisting}

有2种使用方式
\begin{lstlisting}
std::thread t1(foo);
std::thread t2(bar);
std::swap(t1, t2);
t1.swap(t2);
\end{lstlisting}

\subsubsection{thread对象不可复制}
\verb|thread|对象是不能复制的，他的源代码中，删除了拷贝赋值运算符。

但是保留了移动赋值运算符，所以\verb|thread|只能移动，不能拷贝。
\begin{lstlisting}
thread& operator=(const thread&) = delete;

thread& operator=(thread&& __t) noexcept
{
    if (joinable())
       std::terminate();
    swap(__t);
    return *this;
}
\end{lstlisting}

\paragraph{容器与thread对象}
容器存储线程时，比如\verb|vector|,如果用\verb|push_back|操作势必会调用\verb|std::thread|，这样会引发编译错误，因为\verb|std::thread|没有拷贝构造函数。我们在之前网络编程实现IOServicePool或者IOThreadPool时初始化了多个线程存储在vector中, 采用的时emplace方式，可以直接根据线程构造函数需要的参数构造，这样就避免了调用thread的拷贝构造函数。

这主要也体现了\verb|push_back|和\verb|emplace_back|之间的区别

\begin{lstlisting}
void use_vector() {
    std::vector<std::thread> threads;
    for (unsigned i = 0; i < 10; ++i) {
        threads.emplace_back(param_function, i);
    }
    for (auto& entry : threads) {
        entry.join();
    }
}
\end{lstlisting}

\subsubsection{thread对象的生命周期，禁止拷贝，移动语义}
\verb|thread|对象之间，也有主从关系，是根据创建它的作用域层级来判定的。创建了\verb|thread|对象的作用域如果结束了，势必要调用\verb|thread|对象的析构函数以清理其内存。这就引出了\verb|thread|对象的生命周期的管理问题，解决一些复杂情形，比如\verb|thread|对象创建后移交控制权或者说生命周期的问题。

\paragraph{不要将线程管理权移交给另外一个正在运行的线程对象}
还是回到上面的代码示例中\verb|t = std::move(t2);|这一句。\verb|t|是已经绑定了一个函数正在运行的线程，而此时再给他移交一个另外一个已经绑定了函数正在运行的线程的管理权，会发生错误。

\paragraph{案例分析 - 使用std::move移动线程对象}
看下面的代码示例，将lambda表达式传递给\verb|thread|的构造函数创建了2个线程\verb|t|和\verb|t2|。

在\verb|t = std::move(t2);|语句上，编译期可能会给出警告，但是运行时一定会报错。

我在这里加上了\verb|while (1) {}|，以便于让该线程可以一只运行。
\begin{lstlisting}
#include <iostream> 
#include <thread> 
#include <utility>

int main() {
    std::thread t([] {
        std::cout << std::this_thread::get_id() << std::endl;
        while (1) {}
    });
    std::thread t2([] {
        std::cout << std::this_thread::get_id() << std::endl;
        while (1) {}
    });
    t = std::move(t2);
    t.join();
    std::cout << std::boolalpha << "t2.joinable(): " << t2.joinable() << std::endl;
    system("pause");
}  
\end{lstlisting}

使用gdb在\verb|t = std::move(t2);|一行加上断点。运行到断点时，通过\verb|info local|查询本地变量。我们可以看到两个\verb|thread|对象，及其id。
\begin{lstlisting}
(gdb) info local
t = {_M_id = {_M_thread = 140737348015872}}
t2 = {_M_id = {_M_thread = 140737339623168}}
\end{lstlisting}

输入\verb|n|运行下一行，显示了以下报错信息。我们把\verb|t2|交给\verb|t|来接管，那么\verb|t2|对象由于没有需要运行的内容了，所以就析构了，产生了\verb|SIGABRT|信号。
\begin{verbatim}
(gdb) n
140737339623168
terminate called without an active exception

Thread 1 "a.out" received signal SIGABRT, Aborted.
__GI_raise (sig=sig@entry=6) at ../sysdeps/unix/sysv/linux/raise.c:50
50	../sysdeps/unix/sysv/linux/raise.c: No such file or directory.
\end{verbatim}

我们再看一下产生信号前的函数调用堆栈，以及2个\verb|thread|对象的地址。

执行\verb|operator=|的\verb|thread|对象的地址是\verb|0x7fffffffdc88|，正是\verb|t2|对象随后就调用了\verb|std::terminate()|函数。
\begin{verbatim}
(gdb) p &t2
$2 = (std::thread *) 0x7fffffffdc90
(gdb) p &t
$3 = (std::thread *) 0x7fffffffdc88
(gdb) n
140737339623168
terminate called without an active exception

Thread 1 "a.out" received signal SIGABRT, Aborted.
__GI_raise (sig=sig@entry=6) at ../sysdeps/unix/sysv/linux/raise.c:50
50	../sysdeps/unix/sysv/linux/raise.c: No such file or directory.
(gdb) bt
#0  __GI_raise (sig=sig@entry=6) at ../sysdeps/unix/sysv/linux/raise.c:50
#1  0x00007ffff7ba0859 in __GI_abort () at abort.c:79
#2  0x00007ffff7e298d1 in ?? () from /lib/x86_64-linux-gnu/libstdc++.so.6
#3  0x00007ffff7e3537c in ?? () from /lib/x86_64-linux-gnu/libstdc++.so.6
#4  0x00007ffff7e353e7 in std::terminate() () from /lib/x86_64-linux-gnu/libstdc++.so.6
#5  0x0000555555556025 in std::thread::operator= (this=0x7fffffffdc88, __t=...) at /usr/include/c++/9/thread:152
#6  0x0000555555555419 in main () at ThreadJoinable1.cpp:14
\end{verbatim}

查询\verb|thread|库的源代码，查找\verb|operator=|的部分。发现里面确实调用了\verb|std::terminate()|
\begin{lstlisting}
thread& operator=(thread&& __t) noexcept {
    if (joinable())
        std::terminate();
    swap(__t);
    return *this;
}
\end{lstlisting}

\subsubsection{线程守卫 - 自动join，(C++20)jthread}\label{线程守卫 - 自动join，(C++20)jthread}
可以简化编程过程。在线程析构之前\verb|join|，确保该线程不会提前释放。

\begin{lstlisting}
class thread_guard{
private:
    std::thread &_t;
public:
    explicit thread_guard(std::thread &t): _t(t){}
    ~thread_guard();{
        if( _t.joinable() ) {
            _t.join();
        }
    }

    thread_guard(thread_guard const&) = delete;
    thread_guard& operator=(thread_guard const &) = delete;
}
\end{lstlisting}

\paragraph{手动实现joinable thread}
代码示例\ref{手动实现joinable thread代码示例1}给出了一份joinable thread的实现方案，来自于llfc的博客及gitee(\href{https://gitee.com/secondtonone1/boostasio-learn/blob/master/concurrent/day01-thread/day01-thread.cpp}{gitee链接})

代码示例\ref{手动实现joinable thread代码示例1}中的有些中文注释是我加上去的。

\begin{code_example}\label{手动实现joinable thread代码示例1}
\begin{lstlisting}
class joining_thread {
    std::thread  _t;
public:
    // 禁止编译器生成默认构造函数
    joining_thread() noexcept = default;
    // 模板函数，接收一个类型参数Callable和一个参数包... Args
    // explicit表示，这个构造函数，必须显式调用。
    // 通过完美转发std::forward，将Callable和... Args传递给std::thread的构造函数，构造成员_t
    template<typename Callable, typename ... Args>
    explicit joining_thread(Callable&& func, Args&& ...args):
        _t(std::forward<Callable>(func),  std::forward<Args>(args)...){}
    // 可以接收1个std::thread对象来构造joining_thread对象
    // 通过std::move将std::thread对象移动到成员_t中
    explicit joining_thread(std::thread t) noexcept: _t(std::move(t)){}
    // 移动构造函数
    joining_thread(joining_thread&& other) noexcept: _t(std::move(other._t)){}
    // 移动赋值运算符
    // 先对当前的线程调用join()，防止了之前发生的向正在运行的thread对象上
    // 转移另外一个thread对象的情况
    joining_thread& operator=(joining_thread&& other) noexcept
    {
        // 如果当前线程可汇合，则汇合等待线程完成再赋值
        if (joinable()) {
            join();
        }
        _t = std::move(other._t);
        return *this;
    }
    // 与上面的类似
    joining_thread& operator=(std::thread other) noexcept
    {
        //如果当前线程可汇合，则汇合等待线程完成再赋值
        if (joinable()) {
            join();
        }
        _t = std::move(other);
        return *this;
    }
    // 析构函数，析构前调用join()    
    ~joining_thread() noexcept {
        if (joinable()) {
            join();
        }
    }
    // 与std::thread相同的swap()接口
    void swap(joining_thread& other) noexcept {
        _t.swap(other._t);
    }
    // 与std::thread相同的get_id()接口
    std::thread::id   get_id() const noexcept {
        return _t.get_id();
    }
    
    bool joinable() const noexcept {
        return _t.joinable();
    }
    
    void join() {
        _t.join();
    }
    
    void detach() {
        _t.detach();
    }
    
    std::thread& as_thread() noexcept {
        return _t;
    }
    
    const std::thread& as_thread() const noexcept {
        return _t;
    }
};
\end{lstlisting}
\end{code_example}

\paragraph{(C++20)向着jthread发展}
曾经有一份C++17标准的备选提案，主张引入新的类\verb|joining_thread|，它与 \verb|std::thread| 类似，但只
要其执行析构函数，线程即能自动汇合，这点与\verb|scoped_thread|非常像。可惜C++标准委员会未
能达成共识，结果C++17标准没有引入这个类，后来它改名为\verb|std::jthread|，依然进入了C++20标
准的议程 (现已被正式纳入C++20标准)。除去这些，实际上joining thread类的代码相对容易编
写。

\subsection{std::this\_thread}
C++11提供了一个命名空间\verb|this_thread|来引用当前线程，该命名空间有4个有用的函数，\verb|get_id|，\verb|yield|，\verb|sleep_until|，\verb|sleep_for|

\paragraph{get\_id()}
获取线程id

\paragraph{sleep\_for()}
阻塞当前线程一段时间

\begin{lstlisting}
this_thread::sleep_for(chrono::nanoseconds(1000));//阻塞当前线程1000纳秒
this_thread::sleep_for(chrono::microseconds(1000));//阻塞当前线程1000微妙
this_thread::sleep_for(chrono::milliseconds(1000));//阻塞当前线程1000毫秒
this_thread::sleep_for(chrono::seconds(20)+ chrono::minutes(1));//阻塞当前线程1分钟20秒
this_thread::sleep_for(chrono::hours(1));//阻塞当前线程1小时
\end{lstlisting}

\paragraph{sleep\_until()}
\verb|sleep_until()|

示例代码如下

效果是主线程从\verb|sleep\_until|处开始挂起，然后到下一个整分时间再继续执行
\begin{lstlisting}
#include <iostream>       // std::cout
#include <thread>         // std::this_thread::sleep_until
#include <chrono>         // std::chrono::system_clock
#include <ctime>          // std::time_t, std::tm, std::localtime, std::mktime
#include <time.h>
#include <stddef.h>
using namespace std;

void getNowTime()   //获取并打印当前时间
{
    timespec time;
    struct  tm nowTime;
    clock_gettime(CLOCK_REALTIME, &time);  //获取相对于1900到现在的秒数
    
    localtime_r(&time.tv_sec, &nowTime);
    char current[1024];
    printf(
    "%04d-%02d-%02d %02d:%02d:%02d\n",
    nowTime.tm_year + 1900,
    nowTime.tm_mon+1,
    nowTime.tm_mday,
    nowTime.tm_hour,
    nowTime.tm_min,
    nowTime.tm_sec);
}
int main()
{
    using std::chrono::system_clock;
    std::time_t tt = system_clock::to_time_t(system_clock::now());
    
    struct std::tm * ptm = std::localtime(&tt);
    getNowTime();   // 打印当前时间
    cout << "Waiting for the next minute to begin...\n";
    ++ptm->tm_min; //加一分钟
    ptm->tm_sec = 0; //秒数设置为0
    //暂停执行，到下一整分执行
    this_thread::sleep_until(system_clock::from_time_t(mktime(ptm)));
    getNowTime();   //打印当前时间
    
    return 0;
}    
\end{lstlisting}

运行结果
\begin{lstlisting}
[root@localhost test] g++ -o test test.cpp -lpthread -std=c++11
[root@localhost test] ./test
2019-04-13 08:24:29
Waiting for the next minute to begin...
2019-04-13 08:25:00    
\end{lstlisting}

\subsection{yield()}
\begin{lstlisting}
void yield() noexcept; 
\end{lstlisting}
让出CPU时间，让CPU在切换各个线程时，本轮次先不运行此线程的计算，这个过程称为"让出自己的CPU时间"

\subsubsection{使用示例}
比如说你的线程需要等待某个操作完成，如果你直接用一个循环不断判断这个操作是否完成就会使得这个线程占满CPU时间，这会造成资源浪费。这时候你可以判断一次操作是否完成，如果没有完成就调用yield交出时间片，过一会儿再来判断是否完成，这样这个线程占用CPU时间会大大减少。
\begin{lstlisting}
while(!isDone()); // Bad
while(!isDone()) yield(); // Good
\end{lstlisting}

\subsubsection{具体行为仍然由操作系统决定}
需要注意的是，\verb|std::this_thread::yield()| 并不能保证当前线程一定会让出 CPU 时间片，因为具体的调度策略是由操作系统决定的。此外，\verb|std::this_thread::yield()| 也不能保证其他线程一定会立即运行，因为其他线程可能还处于阻塞状态。

\subsubsection{与sleep\_for()的区别}
\verb|yield()|方法让出CPU的时间是不确定的，并且以CPU调度时间片为单位。而\verb|sleep_for()|让出CPU的时间是固定的。\verb|sleep_for()|将阻塞线程至少要求的时间量。

\verb|yield()|的实现依赖于操作系统CPU调度策略，在不同的操作系统或者同一个操作系统的不同调度策略下，表现也可能是不同的。

\subsection{传递临时对象(临时变量)作为线程参数}
向子线程传递变量时，采用的是按值传递的方式。与线程接口函数的形参写法，没有关系。

按值传递，保证了线程如果detach了，也可以正常使用原来传入的参数。

\subsubsection{向线程传参是按值传递}
代码示例
\begin{lstlisting}
#include <thread>
#include <iostream>

void myPrint(const int &i, char * p_mybuf)
{
    std::cout << "i = " << i << std::endl;
    std::cout << "&i = " << &i << std::endl;
    std::cout << "p_mybuf = " << p_mybuf << std::endl;
    return;
}

int main()
{
    int mvar = 1;
    int &mvary = mvar;
    char mybuf[] = "this is a test!";
    std::cout << "&mvar = " << &mvar << std::endl;
    std::cout << "&mvary = " << &mvary << std::endl;
    std::thread my_thread(myPrint, mvar, mybuf);
    
    my_thread.detach();
    std::this_thread::sleep_for(std::chrono::milliseconds(1000));
    
    std::cout << "main thread runs here. " << std::endl;
    
    return 0;
}    
\end{lstlisting}

程序的输出结果如下

传入的整形变量，其地址为0x7fffffffdb30，但是在子线程中，其地址变为了0x55555576b290

说明，就算是线程入口函数的形参，写成了按引用接受传参的形式，仍然实质上发生的是按值传递。
\begin{lstlisting}
&mvar = 0x7fffffffdb30
&mvary = 0x7fffffffdb30
i = 1
&i = 0x55555576b290
p_mybuf = this is a test!
main thread runs here. 
\end{lstlisting}

\subsubsection{尽量少使用指针向子线程传值}
用指针向子线程传值时，如果再加上detach，容易发生不可预知的后果。

比如上一个代码示例中函数\verb|void myPrint(const int &i, char * p_mybuf)|的第二个参数。

尽量少使用指针向子线程传值。

在进行并发开发时，要注意避免发生不可预料的情况，要尽可能提升确定性，降低不确定性。

\subsubsection{避免发生隐式类型转换}
如下面所示的代码，就是会发生隐式类型转换的一个例子。

主线程向子线程传参时，发生了一个从\verb|char[]|向\verb|std::string|的隐式类型转换

代码示例

\begin{lstlisting}
#include <thread>
#include <iostream>

void myPrint(const int &i, const std::string p_mybuf)
{
    std::cout << "i = " << i << std::endl;
    std::cout << "p_mybuf = " << p_mybuf << std::endl;
    return;
}

int main()
{
    int mvar = 1;
    int &mvary = mvar;
    char mybuf[] = "this is a test!";
    std::thread my_thread(myPrint, mvar, mybuf);
    
    my_thread.detach();
    std::this_thread::sleep_for(std::chrono::milliseconds(1000));
    
    std::cout << "main thread runs here. " << std::endl;
    
    return 0;
}
\end{lstlisting}

为了避免这种情况，可以做如下改写，即，在向子线程传参时，使用\verb|string(mybuf)|的形式。

\subsection{(C++20)jthread}
\verb|std::jthread|是c++20所支持的新的线程类型，他会在析构的时候调用\verb|join()|。

与\verb|std::jthread|一同提出的，还有一些线程停止的灵活工具，详见第\ref{(C++20)线程取消机制}节。

定义在头文件\verb|<thread>|中。

与\verb|std::thread|类似，也有\verb|joinable|，\verb|get_id|，\verb|native_handle|，\verb|hardware_concurrency|，\verb|join|，\verb|detach|，\verb|swap|这些接口。

\subsection{(C++20)线程取消机制}\label{(C++20)线程取消机制}
C++20中，提出了一些用于优化线程停止过程的新特性
\begin{itemize}
    \item \verb|std::stop_token|：这是一个类型，用于表示线程的停止请求状态。它可以被传递给线程函数，使线程能够检查是否收到了停止请求。如果收到停止请求，线程可以选择合适地终止执行。
    \item \verb|std::stop_source|：这是一个停止源，它维护了一个共享的停止状态。每个\verb|std::jthread|对象内部都持有一个\verb|std::stop_source|实例。\verb|request_stop()|方法会对持有指定\verb|std::stop_source|对象的所有线程，改变其停止状态。
    \item \verb|std::stop_callback|：存储一个可调用对象，在\verb|std::stop_token|收到停止请求之后调用。主要负责解决这个线程退出时需要做什么事情的需求。
\end{itemize}

\subsubsection{与传统std::thread的区别}
\verb|std::thread|是没有提供良好的退出管理机制的，他如果想要正常退出，必须在运行完他绑定的函数调用后才会推出。而且退出后需要执行什么行为，C++11标准中并未给出相关机制，需要开发者自行补充。

而C++20提出的线程取消机制，与\verb|std::jthread|相配合，为线程什么时候退出，怎样控制线程退出，线程退出之后还会做什么行为等需求，提供了编程语言标准层面支持的解决方案。

\subsection{stop\_source与stop\_token}
\verb|std::stop_source|是停止命令的发起者，发起停止是通过调用他的方法\verb|request_stop()|实现的。

调用\verb|get_token()|方法，返回一个\verb|std::stop_token|对象。这个对象通过\verb|stop_requested()|方法可以获知\verb|std::stop_source|是否发起停止命令。

\verb|std::stop_source|对象可以派发给多个\verb|jthread|，每个\verb|jthread|在其作用域内生成自己的\verb|std::stop_token|对象。

\paragraph{代码示例}
看下面的代码示例。

创建了4个\verb|jthread|对象，并且有一个具名的lambda表达式\verb|print|，打印\verb|std::stop_source|的\verb|stop_possible()|和\verb|stop_requested()|方法的结果。

这4个\verb|jthread|对象是公用1个\verb|std::stop_source|对象的。

给4个\verb|jthread|对象分配任务，把\verb|std::stop_source|对象也传入。

在线程内部，通过\verb|std::stop_source|对象的\verb|get_token()|方法，获得\verb|std::stop_token|。

线程内部，是一个循环，有一个判断分支，其条件就是\verb|stoken.stop_requested()|。

在主线程中对\verb|std::stop_source|对象调用\verb|request_stop()|方法时，会传递给基于这个\verb|std::stop_source|对象生成的所有\verb|std::stop_token|上，此时其\verb|stop_requested()|结果为\verb|false|，则会运行判断分支中的代码。

这时再运行具名lambda表达式\verb|print|，发现\verb|stop_requested()|的结果变为\verb|true|了
\begin{lstlisting}
#include <chrono>
#include <iostream>
#include <stop_token>
#include <thread>

using namespace std::chrono_literals;

void worker_fun(int id, std::stop_source stop_source)
{
    std::stop_token stoken = stop_source.get_token();
    for (int i = 10; i; --i)
    {
        std::this_thread::sleep_for(300ms);
        if (stoken.stop_requested())
        {
            std::printf("    worker%d is requested to stop\n", id);
            return;
        }
        std::printf("    worker%d goes back to sleep\n", id);
    }
}

int main()
{
    std::jthread threads[4];
    std::cout << std::boolalpha;
    auto print = [](const std::stop_source& source)
    {
        std::printf("stop_source stop_possible = %s, stop_requested = %s\n",
                    source.stop_possible() ? "true" : "false",
                    source.stop_requested() ? "true" : "false");
    };
    
    // Common source
    std::stop_source stop_source;
    
    print(stop_source);
    
    // Create worker threads
    for (int i = 0; i < 4; ++i)
        threads[i] = std::jthread(worker_fun, i + 1, stop_source);
    
    std::this_thread::sleep_for(500ms);
    
    std::puts("Request stop");
    stop_source.request_stop();
    
    print(stop_source);
    
    // Note: destructor of jthreads will call join so no need for explicit calls
}
\end{lstlisting}

程序输出
\begin{lstlisting}
stop_source stop_possible = true, stop_requested = false
    worker2 goes back to sleep
    worker3 goes back to sleep
    worker1 goes back to sleep
    worker4 goes back to sleep
Request stop
stop_source stop_possible = true, stop_requested = true
    worker3 is requested to stop
    worker1 is requested to stop
    worker2 is requested to stop
    worker4 is requested to stop
\end{lstlisting}

\section{互斥}
\subsubsection{为什么引入互斥的概念}
多个线程要处理同一块公共资源，就需要对公共资源进行保护。

某一线程写入公共数据，另一线程调用该公共数据，那么就要确保，后一线程要在前一线程进行写操作结束之后才去进行读操作。

如果不进行这种管理，就可能发生"线程不安全的操作"

\subsection{C++11提供的互斥}
\subsubsection{互斥量}
C++11提供了4种互斥量
\begin{enumerate}
    \item \verb|std::mutex|:最基本的mutex类
    \item \verb|std::recursive_mutex|:递归mutex类，能多次锁定而不死锁
    \item \verb|std::time_mutex|:定时mutex类，可以锁定一定的时间
    \item \verb|std::recursive_timed_mutex|:定时递归
\end{enumerate}

\subsubsection{锁类型}
另外还提供两种锁类型
\begin{enumerate}
    \item \verb|std::lock_guard|:方便线程对互斥量上锁
    \item \verb|std::unique_lock|:方便线程对互斥量上锁，但提供了更好的上锁和解锁控制
    \item (C++17)\verb|scope_lock|
\end{enumerate}

\subsubsection{函数}
\begin{enumerate}
    \item \verb|std::try_lock|:尝试同时对多个互斥量上锁
    \item \verb|std::lock|:可以同时对多个互斥量上锁
    \item \verb|std::call_once|:如果多个线程需要同时调用某个函数，\verb|std::call_once|可以保证多个线程对该函数只调用一次
\end{enumerate}

\subsection{使用互斥量与互斥锁}
\subsubsection{std::mutex}
构造函数:\verb|std::mutex|不允许拷贝构造，也不允许\verb|move|拷贝，最初产生的\verb|mutex|对象是处于\verb|unlocked|状态的。

进行数据上锁\verb|mtx.lock();|，进行数据解锁\verb|mtx.unlock();|。

"数据上锁"和"数据解锁"之间的代码，称为"临界区"(critical region)，也叫"临界段代码"(critical sections)，是指那些必须完整运行，不可被打断的代码段。

\paragraph{lock()}
调用\verb|std::mutex|类的方法\verb|lock()|会发生如下三件事
\begin{enumerate}
    \item 如果该互斥量当前没有被锁住，则调用线程将该互斥量锁住，直到调用unlock之前，该线程一直拥有该锁。
    \item 如果当前互斥量被其他线程锁住，则当前的调用线程被阻塞住。
    \item 如果当前互斥量被当前调用线程锁住，则会产生死锁,，也就是说同一个线程中不允许锁两次。
\end{enumerate}

\paragraph{try\_lock()}
尝试锁住互斥量，如果互斥量被其他线程占有，则当前线程也不会被阻塞，线程调用该函数会出现下面3种情况
\begin{enumerate}
    \item 如果当前互斥量没有被其他线程占有，则该线程锁住互斥量，直到该线程调用unlock释放互斥量。
    \item 如果当前互斥量被其他线程锁住，则当前调用线程返回false，而并不会被阻塞掉。
    \item 如果当前互斥量被当前调用线程锁住，则会产生死锁。
\end{enumerate}

\subsubsection{std::call\_once}
\verb|std::call_once|与\verb|std::once_flag|是搭配使用的，都位于头文件\verb|<mutex>|中，设计他们的出发点是保护资源的初始化过程，确保他们仅被初始化一次。

\verb|std::once_flag|用于表示和管理调用的次数，也就是说，不需要程序员手动用一个\verb|bool|变量来记录这个次数。

\verb|std::call_once|保证函数只被执行一次，如果有多个线程同时执行函数fn调用，则只有一个活动线程(active call)会执行函数，其他的线程在这个线程执行返回之前会处于”passive execution”(被动执行状态)——不会直接返回，直到活动线程对传入函数的调用结束才返回。对于所有调用函数的并发线程，数据可见性都是同步的(一致的)。

如果活动线程在执行fn时抛出异常，则会从处于”passive execution”状态的线程中挑一个线程成为活动线程继续执行fn，依此类推。一旦活动线程返回，所有”passive execution”状态的线程也返回,不会成为活动线程。(实际上\verb|std::call_once|相当于一个锁，使用它的线程都会在上面等待，只有一个线程允许执行。如果该线程抛出异常，那么从等待中的线程中选择一个，重复上面的流程)。

\verb|std::once_flag|的生命周期，它必须要比使用它的线程的生命周期要长。所以通常定义成全局变量比较好。 

\verb|std::once_flag|禁止拷贝，也禁止移动。

\paragraph{函数声明}
\verb|std::call_once|的函数声明如下
\begin{lstlisting}
template< class Callable, class... Args >
void call_once( std::once_flag& flag, Callable&& f, Args&&... args );
\end{lstlisting}

\verb|Callable&& f|传入一个可调用对象。

\verb|Args&&... args|传入可调用对象的参数。

\subsubsection{std::recursive\_mutex}

\verb|std::recursive_mutex|，又称为递归锁，与\verb|std::mutex|类似，但是它能够进行多次lock，这样能够规避一些死锁问题

\begin{enumerate}
    \item 若\verb|std::recursive_mutex|对象当前处于未上锁状态，则调用线程会对其上锁
    \item 若\verb|std::recursive_mutex|对象被其他线程上锁，则调用线程将阻塞，直到该对象被解锁
    \item 若\verb|std::recursive_mutex|对象已经被该调用线程上锁，调用线程再次调用该函数，会获得对该\verb|std::recursive_mutex|对象新的所有权级。而完全解锁该\verb|std::recursive_mutex|对象需要调用相同次数的unlock函数
\end{enumerate}

代码示例
\begin{lstlisting}
int counter = 0;
std::recursive_mutex mtx;
void func2()
{
    mtx.lock();
    counter++;
    cout <<"func2 thread_id  " << std::this_thread::get_id() << "  "
                <<"全局变量 counter  " << counter << endl;
    mtx.unlock();
}
void func1()
{
    mtx.lock();
    func2();
    counter++;
    cout <<"func1 thread_id  " << std::this_thread::get_id() << "  "
                <<"全局变量 counter  " << counter << endl;
    mtx.unlock();
}

int main()
{
    std::thread threads[10];
    
    for (int i = 0; i < 10; ++i)
    {
        threads[i] = std::thread(func1);
    }
    
    for (auto& th : threads)
    {
        th.join();
    }
    std::cout << counter << std::endl;   
    return 0;
}
\end{lstlisting}

\subsubsection{(C++14)std::shared\_timed\_mutex}
能够提供共享访问模式的互斥量，普通的互斥量只有排他性访问模式，或者说独占访问模式。

\verb|std::shared_timed_mutex|能够提供2个层次的访问模式，共享和独占。

共享访问模式使用如下的接口
\begin{itemize}
    \item \verb|lock_shared|
    \item \verb|try_lock_shared|
    \item \verb|try_lock_shared_for|
    \item \verb|try_lock_shared_until|
    \item \verb|unlock_shared|
\end{itemize}

\subsubsection{(C++17)std::shared\_mutex}
\verb|std::shared_mutex|不带有超时功能，所以性能更优，而\verb|std::shared_timed_mutex|带有超时功能。

\subsubsection{std::lock\_guard}

\verb|std::lock_guard|的常见写法是\verb|lock_guard<mutex> guard(mt);|

\verb|std::lock_guard|的设计理念是在\verb|lock_guard|构造函数里加锁，在析构函数里解锁。这是一个体现RAII思想的实践案例。

之所以搞了这个写法，C++委员会的解释是防止使用mutex加锁解锁的时候，忘记解锁unlock了。

那么什么时候会调用析构函数呢？以常见的情况简单来说，就是在一个代码块结束之后调用，即遇到一个右花括号\verb|}|的时候

\paragraph{缺陷}
这就产生了一个问题，如果这个定义域范围很大的话，那么锁的粒度就很大，很大程序上会影响效率。所以为了解决\verb|lock_guard|锁的粒度过大的原因，\verb|unique_lock|就出现了。

\subsubsection{std::unique\_lock}
\verb|unique_lock|与\verb|lock_guard|的区别在于，他可以在当前作用域结束之前，手动解锁，手动执行\verb|unlock()|函数。

\subsubsection{(C++14)std::shared\_lock}
以共享模式锁定互斥量。

可以移动但是不能复制。

与条件变量搭配时，需要使用\verb|std::condition_variable_any|。

类似地，\verb|std::unique_lock|与\verb|std::condition_variable|搭配使用。

\subsubsection{(C++17)std::scope\_lock}
\verb|std::scoped_lock|的预防死锁策略很简单，假设要对n个mutex(mutex1, mutex2, ..., mutexn)上锁，那么每次只尝试对一个mutex上锁，只要上锁失败就立即释放获得的所有锁(方便让其他线程获得锁)，然后重新开始上锁，处于一个循环当中，直到对n个mutex都上锁成功。

\paragraph{缺陷}
这种策略是基本上是有效的，虽然有极小的概率出现“活锁”，例如上面的ABBA死锁中，线程1释放锁A的同一时刻时线程2又释放了锁B，然后这两个线程又同时分别获得了锁A和锁B，如此循环。

\subsection{死锁问题}
\subsubsection{死锁现象}
可以声明多个互斥量。

但是要注意，防止发生死锁现象。

\subsubsection{死锁现象发生的原因}
\begin{enumerate}
\item 没有及时解锁
\item 上锁顺序不同
\end{enumerate}
对于多个互斥量的上锁，可以使用\verb|std::lock(mtx1, mtx2);|的形式，但是解锁还是要逐个操作。

\subsubsection{将加锁、解锁操作做好封装}
实际工作中避免死锁的一个方式就是将加锁和解锁的功能封装为独立的函数，这样能保证在独立的函数里执行完操作后就解锁，不会导致一个函数里使用多个锁的情况

\begin{lstlisting}
//加锁和解锁作为原子操作解耦合，各自只管理自己的功能
void atomic_lock1() {
    std::cout << "lock1 begin lock" << std::endl;
    t_lock1.lock();
    m_1 = 1024;
    t_lock1.unlock();
    std::cout << "lock1 end lock" << std::endl;
}
void atomic_lock2() {
    std::cout << "lock2 begin lock" << std::endl;
    t_lock2.lock();
    m_2 = 2048;
    t_lock2.unlock();
    std::cout << "lock2 end lock" << std::endl;
}
void safe_lock1() {
    while (true) {
        atomic_lock1();
        atomic_lock2();
        std::this_thread::sleep_for(std::chrono::milliseconds(5));
    }
}
void safe_lock2() {
    while (true) {
        atomic_lock2();
        atomic_lock1();
        std::this_thread::sleep_for(std::chrono::milliseconds(5));
    }
}
void test_safe_lock() {
    std::thread t1(safe_lock1);
    std::thread t2(safe_lock2);
    t1.join();
    t2.join();
}
\end{lstlisting}

\subsubsection{同时加锁}
当无法避免地在同一个函数中使用两个互斥锁的情况下，那么可以采用同时加锁的方法。

\paragraph{领养锁std::adopt\_lock}
领养锁的机制，\verb|std::adopt_lock|。本来\verb|std::lock_guard|在管理互斥锁的时候，需要负责互斥锁的上锁与解锁。而如果在构造函数中的第2个参数里传入\verb|std::adopt_lock|，则他就不需要负责上锁动作了，只需要在析构时负责解锁就行了。

\begin{lstlisting}
std::lock(objm1._mtx, objm2._mtx);
std::lock_guard <std::mutex> gurad1(objm1._mtx, std::adopt_lock);
std::lock_guard <std::mutex> gurad2(objm2._mtx, std::adopt_lock);
\end{lstlisting}

\subsubsection{std::lock()函数}
传入一个或多个锁对象，确保这些锁会被同时锁住。如果同时上锁失败的话，会把当前上锁的锁也解锁。

是避免死锁的一种方式。
\begin{lstlisting}
template< class Lockable1, class Lockable2, class... LockableN >
void lock( Lockable1& lock1, Lockable2& lock2, LockableN&... lockn );  // (since C++11)
\end{lstlisting}

\subsubsection{(C++17)scoped\_lock}
C++17中，对上面的领养锁的机制，进行了优化，提出了\verb|std::scoped_lock|。

以RAII的形式实现了\verb|std::lock()|以及\verb|std::lock_guard|联用的效果，既能确保传入的几个锁可以同时上锁，也可以确保他们在当前作用域结束后会解锁。

\begin{lstlisting}
std::scoped_lock guard(objm1._mtx, objm2._mtx);
// 等价于
// std::scoped_lock<std::mutex, std::mutex> guard(objm1._mtx, objm2._mtx);
\end{lstlisting}

\subsubsection{层级锁}
依旧是解决无法避免地在同一个函数中使用两个互斥锁的问题，如果同时加锁也难以使用的话。那么可以使用另一种技术路线，即层级锁，清晰的规划出上锁的时序，使得程序运行更可控，而且也要保证代码阅读更容易。

\paragraph{代码示例}
代码示例\ref{层级锁}，展示了一种层级锁的设计。设计了类\verb|hierarchical_mutex|来负责管理互斥锁。与普通互斥锁的区别在于，层级锁是带权重的，用权重来反映锁的顺序。如果代码中出现了不按照预先规定的顺序进行上锁的部分，会在编译期就报错。

每一把层级锁，都有一个当前权重值，上一级权重值，也会有一个本线程记录的层级值。加上一把锁，就会把锁的权重值写入到线程的层级值上。

层级锁能保证我们每个线程加锁时，一定是先加权重高的锁。

并且释放时也保证了顺序。

主要原理就是将当前锁的权重保存在线程变量中，这样该线程再次加锁时判断线程变量的权重和锁的权重是否大于，如果满足条件则继续加锁。

\begin{code_example}\label{层级锁}
\begin{lstlisting}
//  https://www.llfc.club/category?catid=225RaiVNI8pFDD5L4m807g7ZwmF#!aid/2UVOC0CihIdfguQFmv220vs5hAG
// 博主: 恋恋风辰2017
//层级锁
class hierarchical_mutex {
    public:
    explicit hierarchical_mutex(unsigned long value) :_hierarchy_value(value),
    _previous_hierarchy_value(0) {}
    hierarchical_mutex(const hierarchical_mutex&) = delete;
    hierarchical_mutex& operator=(const hierarchical_mutex&) = delete;
    void lock() {
        check_for_hierarchy_violation();
        _internal_mutex.lock();
        update_hierarchy_value();
    }
    void unlock() {
        if (_this_thread_hierarchy_value != _hierarchy_value) {
            throw std::logic_error("mutex hierarchy violated");
        }
        _this_thread_hierarchy_value = _previous_hierarchy_value;
        _internal_mutex.unlock();
    }
    bool try_lock() {
        check_for_hierarchy_violation();
        if (!_internal_mutex.try_lock()) {
            return false;
        }
        update_hierarchy_value();
        return true;
    }
    private:
    std::mutex  _internal_mutex;
    //当前层级值
    unsigned long const _hierarchy_value;
    //上一次层级值
    unsigned long _previous_hierarchy_value;
    //本线程记录的层级值
    static thread_local  unsigned long  _this_thread_hierarchy_value;
    void check_for_hierarchy_violation() {
        if (_this_thread_hierarchy_value <= _hierarchy_value) {
            throw  std::logic_error("mutex  hierarchy violated");
        }
    }
    void  update_hierarchy_value() {
        _previous_hierarchy_value = _this_thread_hierarchy_value;
        _this_thread_hierarchy_value = _hierarchy_value;
    }
};
thread_local unsigned long hierarchical_mutex::_this_thread_hierarchy_value(ULONG_MAX);
void test_hierarchy_lock() {
    hierarchical_mutex  hmtx1(1000);
    hierarchical_mutex  hmtx2(500);
    std::thread t1([&hmtx1, &hmtx2]() {
        hmtx1.lock();
        hmtx2.lock();
        hmtx2.unlock();
        hmtx1.unlock();
    });
    std::thread t2([&hmtx1, &hmtx2]() {
        hmtx2.lock();
        hmtx1.lock();
        hmtx1.unlock();
        hmtx2.unlock();
    });
    t1.join();
    t2.join();
}
\end{lstlisting}
\end{code_example}

\subsection{std::unique\_lock}
\subsubsection{与lock\_guard的区别}
他会在构造函数里给数据加锁，然后可以在该代码块的范围内调用他的\verb|unlock()|方法来提前解锁，而不必等到代码块结束系统自动调用析构函数的时候才解锁。方便控制锁的颗粒度。也支持和条件变量配套使用。

\verb|unique_lock|不能够复制，但是可以移动(\verb|std::move|)。(相对应地，\verb|lock_guard|不能够移动，也不能复制)。移动的不是互斥量，而是互斥锁，或者说，互斥锁的所有权。

一旦\verb|mutex|被\verb|unique_lock|管理，加锁和释放的操作就交给\verb|unique_lock|，不能调用\verb|mutex|加锁和解锁，因为锁的使用权已经交给\verb|unique_lock|了。

\subsubsection{缺陷}
\verb|unique_lock|内部会维护一个锁的状态，所以在效率上肯定会比\verb|lock_guard|慢

\subsubsection{owns\_lock - 判断是否持有锁}
\verb|owns_lock()|方法，判断是否持有锁。如果该互斥锁此时正锁着互斥量，则称为其持有锁，如果没有锁住互斥量，则称为其未持有锁。

\begin{lstlisting}
// https://www.llfc.club/category?catid=225RaiVNI8pFDD5L4m807g7ZwmF#!aid/2Up8cDw1DI6otzUxbSdQksUjRua
// 博主: 恋恋风辰2017
//可判断是否占有锁
void owns_lock() {
    //lock可自动解锁，也可手动解锁
    std::unique_lock<std::mutex> lock(mtx);
    shared_data++;
    if (lock.owns_lock()) {
        std::cout << "owns lock" << std::endl;
    }
    else {
        std::cout << "doesn't own lock" << std::endl;
    }
    lock.unlock();
    if (lock.owns_lock()) {
        std::cout << "owns lock" << std::endl;
    }
    else {
        std::cout << "doesn't own lock" << std::endl;
    }
}
\end{lstlisting}

\subsubsection{延迟加锁std::defer\_lock}
在\verb|std::unique_lock|构造函数中传入第2个参数\verb|std::defer_lock|可以实现延迟加锁，也就是说，在运行这个语句的时候，并没有发生实际上的加锁，在后面需要手动\verb|lock()|。

而\verb|unlock()|与一般情况一样，可以自己提前解锁，也可以由析构函数自动解锁。

\begin{lstlisting}
// 博主: 恋恋风辰2017
//可以延迟加锁
void defer_lock() {
    //延迟加锁
    std::unique_lock<std::mutex> lock(mtx, std::defer_lock);
    //可以加锁
    lock.lock();
    //可以自动析构解锁，也可以手动解锁
    lock.unlock();
}
\end{lstlisting}

\subsubsection{锁的颗粒度}
锁的粒度表示加锁的精细程度，一个锁的粒度要足够大，保证可以锁住要访问的共享数据。

同时一个锁的粒度要足够小，保证非共享数据不被锁住影响性能。

由于\verb|std::unique_lock|可以手动解锁，甚至可以延迟手动加锁，所以他为控制颗粒度提供了极大的便利。

\subsection{(C++14, 17)共享锁}
(C++14)\verb|std::shared_time_mutex|

(C++14)\verb|std::shared_lock|

(C++17)\verb|std::shared_mutex|

(C++11)标准没有提供共享锁的机制，但是\verb|boost|中提供了\verb|boost::shared_mutex|

\subsubsection{适用场景 - 读写锁}
对于一个DNS服务，我们可以根据域名查询服务对应的ip地址，它很久才更新一次，比如新增记录，删除记录或者更新记录等。平时大部分时间都是提供给外部查询，对于查询操作，即使多个线程并发查询不加锁也不会有问题，但是当有线程修改DNS服务的ip记录或者增减记录时，其他线程不能查询，需等待修改完再查询。或者等待查询完，线程才能修改。也就是说读操作并不是互斥的，同一时间可以有多个线程同时读，但是写和读是互斥的，写与写是互斥的，简而言之，写操作需要独占锁。而读操作需要共享锁。

简单来说，就是读锁与读锁之间是不互斥的，而读锁与写锁是互斥的。存在一批锁，他们之间是不互斥的，而他们只与另外一批锁互斥。

\paragraph{代码示例}
\begin{lstlisting}
// 博主: 恋恋风辰2017
class DNService {
    public:
    DNService() {}
    //读操作采用共享锁
    std::string QueryDNS(std::string dnsname) {
        std::shared_lock<std::shared_mutex> shared_locks(_shared_mtx);
        auto iter = _dns_info.find(dnsname);
        if (iter != _dns_info.end()) {
            return iter->second;
        }
        return "";
    }
    //写操作采用独占锁
    void AddDNSInfo(std::string dnsname, std::string dnsentry) {
        std::lock_guard<std::shared_mutex>  guard_locks(_shared_mtx);
        _dns_info.insert(std::make_pair(dnsname, dnsentry));
    }
    private:
    std::map<std::string, std::string> _dns_info;
    mutable std::shared_mutex  _shared_mtx;
};
\end{lstlisting}

\section{条件变量}
\subsection{概念}
C++11提供库\verb|<condition_variable>|来实现条件变量

条件变量是利用线程间共享的全局变量进行同步的一种机制，主要包括两个动作

\begin{enumerate}
\item 一个线程等待条件变量的条件成立而挂起
\item 另一个线程使条件成立(给出条件成立信号)
\end{enumerate}

为了防止竞争，条件变量的使用总是和一个互斥量结合在一起。

提供两种条件变量

\begin{itemize}
\item \verb|condition_variable|
\item \verb|condition_variable_any|
\end{itemize}

\paragraph{相同点} 两者都能与\verb|std::mutex|一起使用

\paragraph{不同点} 前者仅限于与 \verb|std::mutex| 一起工作，而后者可以和任何满足最低标准的互斥量一起工作，从而加上了\verb|_any|的后缀。\verb|condition_variable_any|会产生额外的开销。

一般只推荐使用\verb|condition_variable|。除非对灵活性有硬性要求，才会考虑\verb|condition_variable_any|

\paragraph{通知}
\begin{enumerate}
    \item 获取互斥量\verb|std::mutex|, 这个操作通常使用\verb|std::lock_guard|来完成
    \item 在持有锁的期间，在条件变量\verb|std::condition_variable|上执行\verb|notify_one|或者\verb|notify_all|去唤醒阻塞线程
\end{enumerate}

\paragraph{阻塞}
\begin{enumerate}
    \item 使用\verb|std::unique_lock<std::mutex>|来实现加锁操作，使得可以在相同的互斥量\verb|mutex|上(不同的线程)保护共享变量
    \item 执行\verb|wait|,\verb|wait_for| 或 \verb|wait_until|。该操作能够原子性的释放互斥量\verb|mutex|上的锁，并阻塞这个线程
    \item 当条件变量\verb|condition_variable|被通知，超时，或虚假唤醒时，该线程结束阻塞状态，并自动的获取到互斥量\verb|mutex|上的锁。当然，这里应该检查是否为虚假唤醒
\end{enumerate}

\subsection{wait}
%声明一个条件变量\verb|std:condition_variable cv;|

\subsubsection{wait()}
\verb|wait()|的函数原型

\begin{lstlisting}
void wait( std::unique_lock<std::mutex>& lock );

template< class Predicate >
void wait( std::unique_lock<std::mutex>& lock, Predicate stop_waiting );
\end{lstlisting}

根据函数原型，\verb|wait()|只接收\verb|std::unique_lock|，因为\verb|std::unique_lock|可以提前解锁的，而\verb|lock_guard|不行。

第二个参数是可选的，传入一个谓词(这个概念请参见第\ref{函数对象概念}节第\pageref{函数对象概念}页)

\subsubsection{wait\_until()}
和\verb|wait_for()|的区别是，它指定的是一个时间点。到达这个时间点之前处于阻塞状态。

\subsubsection{wait\_for()}
函数声明如下

\begin{lstlisting}
template<class Rep, class Period>
std::cv_status wait_for(std::unique_lock<std::mutex>& lock,
const std::chrono::duration<Rep, Period>& rel_time);

template<class Rep, class Period, class Predicate>
bool wait_for(std::unique_lock<std::mutex>& lock,
const std::chrono::duration<Rep, Period>& rel_time,
Predicate pred);
\end{lstlisting}

\verb|wait_for()|和\verb|wait()|的区别就是，它多了一个功能是指定一段时间。在指定的时间超时之前，该线程都会处于阻塞状态。

\verb|wait_for()|导致当前线程阻塞直至条件变量被通知，或虚假唤醒发生，或者超时返回。

\subsubsection{调用wait()之后发生了什么}
当前线程调用 \verb|wait()| 后将被阻塞(此时当前线程应该获得了锁，假设获得锁\verb|lck|)，直到另外某个线程调用 \verb|notify_*()| 唤醒了当前线程。

在线程被阻塞时，该函数会自动调用 \verb|lck.unlock()| 释放锁，使得其他被阻塞在锁竞争上的线程得以继续执行。另外，一旦当前线程获得通知(\verb|notified|，通常是另外某个线程调用 \verb|notify_*()| 唤醒了当前线程)，\verb|wait()| 函数也是自动调用 \verb|lck.lock()|，使得 \verb|lck| 的状态和 \verb|wait()| 函数被调用时相同。

\subsubsection{如果给出一个谓词作为参数}
如果给出了一个谓词\verb|Predicate pred|，则，只有当\verb|pred|为\verb|false|时调用\verb|wait()|才会阻塞当前线程，并且在收到其他线程的通知时只有当 \verb|pred| 为 \verb|true| 时才会被解除阻塞。

如果收到通知时\verb|pred|为\verb|false|，则线程并不会被唤醒。

而等到\verb|pred|为\verb|true|时，也不会被唤醒，因为需要等待下一个\verb|notify|通知。

设置了谓词的情况，类似于下面的代码
\begin{lstlisting}
while (!pred()) 
    wait(lck);
\end{lstlisting}

易知，收到唤醒后，会进入\verb|while()|循环的判断过程，\verb|pred|为\verb|true|时，才会跳出循环，\verb|pred|为\verb|false|时，仍旧会进入循环。进入循环后，由于唤醒已经被用过了，所以又阻塞在了\verb|wait(lck)|这一行上。

\subsubsection{std::cv\_status}
\verb|std::cv_status|是一个枚举量。
\begin{lstlisting}
enum class cv_status {
    no_timeout,
    timeout  
};
\end{lstlisting}

\paragraph{用法}
\verb|wait_for()|和\verb|wait_until()|都有一个重载，是返回\verb|std::cv_status|的。
\begin{lstlisting}
template< class Rep, class Period >
std::cv_status 
wait_for( std::unique_lock<std::mutex>& lock, 
            const std::chrono::duration<Rep, Period>& rel_time );

template< class Clock, class Duration >
std::cv_status
wait_until( std::unique_lock<std::mutex>& lock,
            const std::chrono::time_point<Clock, Duration>& abs_time );
\end{lstlisting}

所以，\verb|std::cv_status|的用处，就是返回\verb|wait_for()|和\verb|wait_until()|的状态，是因为唤醒而结束等待，还是因为超时而结束等待。

\subsection{notify}
\subsubsection{notify\_one()}
If any threads are waiting on \verb|*this|, calling \verb|notify_one()| unblocks one of the waiting threads. 调用\verb|notify_one()|将会解除一个正在等待这个条件变量的线程的挂起状态。

\verb|notify_one()|的函数声明如下

\begin{lstlisting}
void notify_one() noexcept; 
\end{lstlisting}

\subsubsection{notify\_all()}
\verb|cv.notify_all();|

解锁正在等待当前条件的所有线程，如果没有正在等待的线程，则函数不执行任何操作

\begin{code_example}
\label{代码示例notify_one}
\begin{lstlisting}
#include <iostream>
#include <condition_variable>
#include <thread>
#include <chrono>
using namespace std::chrono_literals;

std::condition_variable cv;
std::mutex cv_m;
int i = 0;
bool done = false;

void waits()
{
    std::unique_lock<std::mutex> lk(cv_m);
    std::cout << "Waiting... \n";
    cv.wait(lk, []{return i == 1;});
    std::cout << "...finished waiting; i == " << i << '\n';
    done = true;
}

void signals()
{
    std::this_thread::sleep_for(200ms);
    std::cout << "Notifying falsely...\n";
    cv.notify_one(); // waiting thread is notified with i == 0.
    // cv.wait wakes up, checks i, and goes back to waiting
    
    std::unique_lock<std::mutex> lk(cv_m);
    i = 1;
    while (!done) 
    {
        std::cout << "Notifying true change...\n";
        lk.unlock();
        cv.notify_one(); // waiting thread is notified with i == 1, cv.wait returns
        std::this_thread::sleep_for(300ms);
        lk.lock();
    }
}

int main()
{
    std::thread t1(waits), t2(signals);
    t1.join(); 
    t2.join();
}
\end{lstlisting}
\end{code_example}

代码示例\ref{代码示例notify_one}可能的输出结果如下

\begin{lstlisting}
Waiting... 
Notifying falsely...
Notifying true change...
...finished waiting; i == 1
...
\end{lstlisting}

\subsubsection{通知只起效1次}
程序创建了两个线程t1和t2(注释1和2处)。

分别对应着\verb|waitingForWork()|和\verb|setDataReady()|。调用\verb|setDataReady()|时进行通知。

通过条件变量\verb|std::condition_variable condVar|使得t2来通知t1他已经完成了前期准备工作。而线程t1则在持有锁的期间等待通知(注释4处)

\begin{code_example}
\label{代码示例notify_one_2}
\begin{lstlisting}
#include <condition_variable>
#include <mutex>
#include <thread>

std::mutex lock;
std::condition_variable condVar;

bool dataReady{false};

void waitingForWork() {
    std::cout << "Waiting ..." << std::endl;
    std::unique_lock<std::mutex> l(lock);
    condVar.wait(l, []{return dataReady;});           // (4)
    std::cout << "Running ..." << std::endl;
}

void setDataReady() {
    {
        std::lock_guard<std::mutex> l{lock};
        dataReady = true;
    }
    std::cout << "Data prepared, notify one" << std::endl;
    condVar.notify_one();                             // (3)
}

int main() {
    std::cout << "==========Begin==========" << std::endl;
    
    std::thread t1(waitingForWork);                    // (1)
    std::thread t2(setDataReady);                      // (2)
    
    t1.join();
    t2.join();
    
    std::cout << "===========End===========" << std::endl;
}
\end{lstlisting}
\end{code_example}

代码示例\ref{代码示例notify_one_2}可能的输出结果如下

\begin{lstlisting}
==========Begin==========
Waiting ...
Data prepared, notify one
Running ...
===========End===========
\end{lstlisting}

在代码\ref{代码示例notify_one_2}中，条件变量的\verb|wait()|函数使用了谓词\verb|[]{return dataReady;}|。这是一个lambda表达式。

使用了谓词的\verb|wait()|函数，在收到通知之后，还会增加一层判断，就是谓词的返回值是否是\verb|true|，如果是\verb|true|则唤醒线程，如果为\verb|false|则仍然处于阻塞状态(等待状态)

等于说，欲被唤醒的线程如果要接触其阻塞状态恢复运行，需要满足两个条件，一个是条件变量自身的\verb|notify()|函数，另一个就是谓词要返回\verb|true|

条件变量带来的"通知"只进行一次，但是谓词进行的判断会以\verb|whilt(1)|的形式不断进行。

\subsubsection{线程安全的队列}
结合模板，实现一个线程安全的队列，入队和出队操作都满足线程安全的要求。

\begin{lstlisting}
// 博主: 恋恋风辰2017
#include <queue>
#include <mutex>
#include <condition_variable>
template<typename T>
class threadsafe_queue
{
    private:
        std::mutex mut;
        std::queue<T> data_queue;
        std::condition_variable data_cond;
    public:
        void push(T new_value)
        {
            std::lock_guard<std::mutex> lk(mut);
            data_queue.push(new_value);
            data_cond.notify_one();
        }
        void wait_and_pop(T& value)
        {
            std::unique_lock<std::mutex> lk(mut);
            data_cond.wait(lk,[this]{return !data_queue.empty();});
            value=data_queue.front();
            data_queue.pop();
        }
};
threadsafe_queue<data_chunk> data_queue;    
void data_preparation_thread()
{
    while(more_data_to_prepare())
    {
        data_chunk const data=prepare_data();
        data_queue.push(data);    ⇽---  ②
    }
}
void data_processing_thread()
{
    while(true)
    {
        data_chunk data;
        data_queue.wait_and_pop(data);   
        process(data);
        if(is_last_chunk(data))
        break;
    }
}
\end{lstlisting}

测试代码如下

\begin{lstlisting}
// 博主: 恋恋风辰2017
void test_safe_que() {
    threadsafe_queue<int>  safe_que;
    std::mutex  mtx_print;
    std::thread producer(
    [&]() {
        for (int i = 0; ;i++) {
            safe_que.push(i);
            {
                std::lock_guard<std::mutex> printlk(mtx_print);
                std::cout << "producer push data is " << i << std::endl;
            }
            std::this_thread::sleep_for(std::chrono::milliseconds(200));
        }
    }
    );
    std::thread consumer1(
    [&]() {
        for (;;) {
            auto data = safe_que.wait_and_pop();
            {
                std::lock_guard<std::mutex> printlk(mtx_print);
                std::cout << "consumer1 wait and pop data is " << *data << std::endl;
            }
            std::this_thread::sleep_for(std::chrono::milliseconds(500));
        }
    }    
    );
    std::thread consumer2(
    [&]() {
        for (;;) {
            auto data = safe_que.try_pop();
            if (data != nullptr) {
                {
                    std::lock_guard<std::mutex> printlk(mtx_print);
                    std::cout << "consumer2 try_pop data is " << *data << std::endl;
                }
            }
            std::this_thread::sleep_for(std::chrono::milliseconds(500));
        }
    }
    );
    producer.join();
    consumer1.join();
    consumer2.join();
}
\end{lstlisting}

\subsection{使用条件变量面临的问题}
无条件的等待可能错过唤醒，简单的唤醒却发现没有事可干。

\subsubsection{虚假唤醒(spurious wakeup)}
在正常情况下，\verb|wait|类型函数返回时要么是因为被唤醒，要么是因为超时才返回，但是在实际中发现，因此操作系统的原因，\verb|wait|类型在不满足条件时，它也会返回，这就导致了\textbf{虚假唤醒(spurious wakeup)}。

有一种说法，异常唤醒和多核处理器有关系，是底层实现的问题，所以试图在应用层解释都是没有意义的。

\paragraph{解决方式}
在使用\verb|wait()|的时候，传入一个谓词

\subsubsection{唤醒丢失}
简单来说就是发出了唤醒但是接收方并未进入准备状态，接收方没有开始接收唤醒的时候发出了唤醒，导致这个唤醒没人接收/没接收到，导致了通知丢失。

\section{原子类型}
\subsection{概念}
\subsubsection{改动序列}
在一个C++程序中，每个对象都具有一个改动序列，它由所有线程在对象上的全部写操作构成，其中第一个写操作即为\textbf{对象的初始化}。

大部分情况下，这个序列会随程序的多次运行而发生变化(指的是，应用层软件必须不对操作系统如何安排线程的运行顺序作出预先的假设)。但是在程序的任意一次运行过程中，所含的全部线程都必须形成相同的改动序列。

对于改动序列，有如下几点基本要求。

\begin{enumerate}
    \item 只要某线程看到过某个对象，则该线程的后续读操作必须获得相对新近的值，并且，该线程就同一对象的后续写操作，必然出现在改动序列后方。
    \item 如果某线程先向一个对象写数据，过后再读取它，那么必须读取前面写的值。
    \item 若在改动序列中，上述读写操作之间还有别的写操作，则必须读取最后写的值。
    \item 在程序内部，对于同一个对象，全部线程都必须就其形成相同的改动序列，并且在所有对象上都要求如此.
    \item 多个对象上的改动序列只是相对关系，线程之间不必达成一致
\end{enumerate}

\subsubsection{原子类型}
C++11提供了库\verb|<atomic>|来实现原子类型

C++中原子类型(atomic)是一种多线程编程中常用的同步机制，它能够确保对共享变量的操作在执行时不会被其他线程的操作干扰，从而避免竞态条件(race condition)和死锁(deadlock)等问题。

原子类型可以看作是一种特殊的类型，它具有类似于普通变量的操作，但是这些操作都是原子级别的，即\textbf{要么全部完成，要么全部未完成}。也就是说，不会出现"一个变量同时读写的时候读到的是并未完成写操作的数据"的情况。在普通的变量中，并发的访问它可能会导致数据竞争，竞争的后果会导致操作过程不会按照正确的顺序进行操作。

C++标准库提供了丰富的原子类型，包括整型、指针、布尔值等，使用方法也非常简单，只需要通过\verb|std::atomic<T>|定义一个原子类型即可，其中T表示变量的类型。例如，\verb|std::atomic<int> globalVariable = 0;|

atomic对象可以通过指定不同的memory orders来控制其对其他非原子对象的访问顺序和可见性，从而实现线程安全。

\subsection{使用原子类型}
\subsubsection{支持的基本操作}
原子类型支持的基本操作有

\begin{enumerate}
    \item 加法：\verb|a += n|或者\verb|a.fetch_add(n)|
    \item 减法：\verb|a -= n|或者\verb|a.fetch_sub(n)|
    \item 与、或、异或运算：\verb|a &= b|、\verb|a |= b|、\verb|a ^= b|或者\verb|a.fetch_and(b)|、\verb|a.fetch_or(b)|、\verb|a.fetch_xor(b)|
    \item 自增、自减运算：\verb|++a|、\verb|--a|、\verb|a++|、\verb|a--|或者\verb|a.fetch_add(1)|、\verb|a.fetch_sub(1)|
    \item 交换：\verb|a.exchange(b)|返回原来的值，将a设置为b
    \item 比较并交换：\verb|a.compare_exchange_strong(b, c)|或者\verb|a.compare_exchange_weak(b, c)|，如果a的值等于b，则将a设置为c，返回\verb|true|，否则返回\verb|false|
\end{enumerate}

\subsubsection{store()}
\verb|std::atomic<T>::store()|是一个成员函数，用于将给定的值存储到原子对象中。

使用方式见下面的代码示例。其中
\begin{itemize}
    \item \verb|desired|是要存储的值
    \item \verb|order|是存储操作的内存顺序，默认是\verb|std::memory_order_seq_cst|
\end{itemize}

\begin{lstlisting}
void store(T desired, std::memory_order order = std::memory_order_seq_cst) volatile noexcept;
void store(T desired, std::memory_order order = std::memory_order_seq_cst) noexcept;
\end{lstlisting}

存储操作的内存顺序参数详见表\ref{存储操作的内存顺序参数}

\subsubsection{load()}
\verb|load()|用于获取原子类型的当前值

有两种使用形式
\begin{enumerate}
    \item 显式调用\verb|load()|函数
    \item 通过重载类型转换运算符实现隐式调用
\end{enumerate}

\begin{lstlisting}
T load(memory_order order = memory_order_seq_cst) const noexcept;
operator T() const noexcept;
\end{lstlisting}

使用示例
\begin{lstlisting}
std::atomic<int> foo (0);

int x;
do {
    x = foo.load(std::memory_order_relaxed);  // get value atomically
} while (x==0);
\end{lstlisting}

\subsubsection{exchange()}
访问和修改包含的值，将包含的值替换并返回它前面的值

\begin{lstlisting}
template< class T >
T exchange( volatile std::atomic<T>* obj, T desired );
\end{lstlisting}

其中，\verb|obj|参数指向需要替换值的\verb|atomic|对象，\verb|desired|参数为期望替换成的值。如果替换成功，则返回原来的值。

\subsubsection{is\_lock\_free}
判断一个\verb|atomic<T>|是否是lock-free类型

\subsubsection{(C++11)std::atomic\_flag}
与\verb|std::atomic<bool>|不同，\verb|std::atomic_flag|不提供\verb|load|和\verb|store|操作

只有一个原子类型\verb|std::atomic_flag|不提供\verb|is_lock_free()|成员函数。类型\verb|std::atomic_flag|的对象在初始化时清零(置为\verb|false|)，随后即可通过成员函数\verb|test_and_set()|查值并设置为\verb|true|，或者由\verb|clear()|设置为\verb|false|。

\verb|clear()|，设置为\verb|false|

\verb|test_and_set()|方法，如果之前是\verb|true|，则无动作，但是返回\verb|true|，如果之前是\verb|false|，则设置为\verb|true|，并返回\verb|false|。返回的是调用\verb|test_and_set()|方法之前所存储的值。

宏定义\verb|ATOMIC_FLAG_INIT|是用于初始化\verb|std::atomic_flag|的方式，在C++11标准中是这么规定的。在C++20之前，这是标准中初始化\verb|std::atomic_flag|的唯一方式，在C++20中已废弃。C++20标准中，在\verb|std::atomic_flag|的默认构造函数中将其自身初始化为\verb|false|而不再需要这个宏定义，但是为了与C兼容而保留了下来。使用案例见代码示例\ref{利用原子类型实现自旋锁}。

\begin{lstlisting}
std::atomic_flag v = ATOMIC_FLAG_INIT;
\end{lstlisting}

\subsubsection{(C++17)is\_always\_lock\_free}
从C++17开始，所有的原子类型都包含一个静态常量表达式成员变量，\verb|std::atomic::is_always_lock_free|。这个成员变量的值表示在任意给定的目标硬件上，原子类型X是否始终以无锁结构形式实现。如果在所有支持该程序运行的硬件上，原子类型X都以无锁结构形式实现，那么这个成员变量的值就为\verb|true|；否则为\verb|false|。

\section{内存序}
\subsection{指令乱序与指令重排}
CPU执行指令，并不是严格按照源代码中的顺序的，尤其是涉及到多核CPU、并发、编译器优化的情况下。会对机器指令进行重新排序。而这个重新排序是否会影响到并发程序的外部运行效果，是需要开发人员注意的事情。

\paragraph{编译器为什么要让指令编程乱序}
简单来说，就是为了填充指令执行期间的迟延，填充指令流水线使他饱和、高效运行。

比如，除法指令，需要很多个时钟周期才能完成。那么就可以把该指令后面某些指令拿到前边来执行。

\paragraph{编译器一般何时会让指令乱序}
一般来说，编译器如果知道了一些指令之间的依赖关系(dependencies)，他就会根据这些依赖关系来重排指令，使他们乱序。

如果某几条指令之间具有很强的依赖关系，那么编译器也不会随意地打乱他们执行的顺序。

比如
\begin{lstlisting}
add $r1, $r2, $3 # r1 <- r2 + r3
xor $r4, $r1, $r2 # r4 <- r1 ^ r2
\end{lstlisting}

后面的xor指令用到了前面add指令的结果(\verb|$r1|)，那么\verb|xor|指令就不会被编译器乱序到\verb|add|的前面，这种依赖称为写后读(RAW，Read-After-Write)

CPU的流水线思想中，除了写后读RAW，还有读后写WAR，写后写WAW。

\paragraph{CPU乱序}
除了编译器在生成汇编语言指令时，会发生乱序之外，CPU在面对1个由指令组成的流水线的时候，也并非死板地逐个执行，而是也会根据某个策略来高效地执行，这就产生了CPU乱序。

指令被解码后，大抵会经过3个过程，发送(issue)，执行(execute)，提交(commmit)。

指令被提交到issue queue中，但是指令执行的周期是不一样长的，较快执行完的指令，其结果就会被暂存到reorder buffer里面。
\begin{figure}[h]\centering
    \caption{CPU乱序}\label{CPU乱序}
    \includegraphics[width=0.8\textwidth]{CPU乱序.jpg}
\end{figure}

从外部来看，具体指令是按照什么顺序执行的，只要看reorder buffer的提交顺序是怎样的就可以了。

\paragraph{对于2个不同的内存地址}
对于两个不同的内存地址A、B，有四种可能的乱序：Load-Load、Load-Store、Store-Load、Store-Store。举例来说，Load-Load乱序表示程序编写序上位于前面的Load(A)可能被乱序到位于后面的Load(B)的后面去。

\paragraph{思考}
所以，如果我们在编程时，不注意指令乱序现象的存在，没有显式地将"指令之间的依赖关系"传递给编译器，那么编译器最终生成的指令流水线，可能不满足我们的需求。

反过来说，我们需要注意到程序中应该存在的依赖关系，然后将这些依赖关系明确地传递给编译器。

\subsection{C++的6种内存序}
C++11引入了6种内存序。这些内存序提供了不同级别的同步保证，可以帮助我们在多线程程序中实现更精细的控制。
\begin{table}[htbp]\centering
    \caption{存储操作的内存顺序参数} \label{存储操作的内存顺序参数}
    \begin{tabular}{llp{26em}}
        \hline
        value & 内存顺序 & 描述 \\ \hline
        \verb|memory_order_relaxed| & 宽松内存序 & 不做任何同步，仅保证该原子类型变量的操作是原子化的，并不保证其对其他线程的可见性和正确性。不保证操作之间的顺序。 \\ \hline
        \verb|memory_order_consume| & 消费者内存序 & 保证本次读取之前所有依赖于该原子类型变量值的操作都已经完成，但不保证其他线程对该变量的存储结果已经可见。用于同步依赖关系。它保证了依赖于原子操作结果的后续操作将按照正确的顺序执行。 \\ \hline
        \verb|memory_order_acquire| & 获取内存序 & 保证本次读取之前所有先于该原子类型变量写入内存的操作都已经完成，并且其他线程对该变量的存储结果已经可见。 \\ \hline
        \verb|memory_order_release| & 释放内存序 & 保证本次写入之后所有后于该原子类型变量写入内存的操作都已经完成，并且其他线程可以看到该变量的存储结果。 \\ \hline
        \verb|memory_order_acq_rel| & 获取-释放内存序 & 结合了获取和释放两种内存序的特点。它既保证了获取操作之后对共享数据的所有读取操作都将看到最新的数据，又保证了在释放操作之前对共享数据的所有写入操作都已完成，并且对其他线程可见。 \\ \hline
        \verb|memory_order_seq_cst| & 顺序一致性的顺序 & 保证本次操作以及之前和之后的所有原子操作都按照一个全局的内存顺序执行，从而保证多线程环境下对变量的读写的正确性和一致性。这是最常用的内存顺序，也是最严格的内存序。 \\ \hline
    \end{tabular}
\end{table}

\verb|memory_order_relaxed|，宽松内存序，没有任何内存屏障。

\verb|memory_order_release|，释放语义，此原子操作之前的读写不能重排至其后，其他线程获得此原子操作的存储结果也必定可见其之前的写入，一般用于写操作，在当前写操作之前插入一个内存屏障，而且通常是写屏障(包括取存屏障和存存屏障)。

\verb|memory_order_acquire|，获得语义，此原子操作之后的读写不能重排到他之前，一般用于读操作，在当前读操作之前插入一个内存屏障，而且通常是读屏障。

\verb|memory_order_consume|，一般用于读操作。

\verb|memory_order_seq_cst|，顺序一致内存序。

\paragraph{内存序的意义}
我们之前讨论了原子操作其原子性的意义，主要在于不被中断和干扰。现在我们要关注大量原子操作的顺序问题。

在多核操作系统重，多个线程同时进行大量读和写操作的时候。我们把视角放在其中一个线程上，他视角下某个共享变量的值，的变化过程，与其他线程对这个共享变量的写的过程，可能是不一致的。这是我们在并发编程之前进行顺序编程时所没有出现过的事情。其他线程可能对某个共享变量进行了修改(即，写操作)，但是由于原子性，另外一个线程，是否能够读得到这个更改，这是一个需要考虑到的问题。而且，当他可以读的到的时候，他读到的值，是否是更改后的最新值。

\paragraph{为什么以前不了解内存序和内存屏障也能写好多线程程序}
如果以前是严格按照规范使用基础库(如\verb|pthread|或者\verb|<thread>|等)所提供的多线程工具，那么就不会因为指令重排和指令乱序而带来的问题。基础库所提供的工具，已经考虑并解决好了内存序问题，他提供了更高层级的抽象，使得其他开发人员在不关注内存序、指令重排和CPU指令集等底层问题时，也能够写好多线程程序。

\subsection{内存序模型}
由上述的6种内存序，可以产生3种内存序模型(ordering model)。

\begin{enumerate}
    \item Sequencial consistent ordering，实现同步，且保证全局的顺序一致性(single total order)。是一致性最强的模型，也通常是默认的内存序模型。
    \item Acquire-release ordering，实现同步，但是不保证全局的顺序一致性。
    \item Relaxed ordering，不实现同步，只保证操作的原子性。
\end{enumerate}

\subsection{利用原子类型实现自旋锁}
在多线程调用时，仅有一个线程在同一时刻进入\verb|test_and_set|，因为\verb|atomic_flag|初始状态为\verb|false|,所以\verb|test_and_set|将\verb|atomic_flag|设置为\verb|true|，并且返回\verb|false|。

比如线程A调用了\verb|test_and_set|返回\verb|false|，这样\verb|lock|函数返回，线程A继续执行加锁区域的逻辑。此时线程B调用\verb|test_and_set|，\verb|test_and_set|会返回\verb|true|,导致线程B在\verb|while|循环中循环等待，达到自旋检测标记的效果。当线程A直行至2处调用\verb|clear|操作后，\verb|atomic_flag|被设置为清空状态，线程B调用\verb|test_and_set|会将状态设为成立并返回\verb|false|，B线程执行加锁区域的逻辑。

我们看到在设置时使用\verb|memory_order_acquire|内存次序，在清除时使用了\verb|memory_order_release|内存次序。

\begin{code_example}\label{利用原子类型实现自旋锁}
\begin{lstlisting}
// 博主: 恋恋风辰2017
#include <iostream>
#include <atomic>
#include <thread>
class SpinLock {
    public:
    void lock() {
        //1 处
        while (flag.test_and_set(std::memory_order_acquire)); // 自旋等待，直到成功获取到锁
    }
    void unlock() {
        //2 处
        flag.clear(std::memory_order_release); // 释放锁
    }
    private:
    std::atomic_flag flag = ATOMIC_FLAG_INIT;
};
\end{lstlisting}
\end{code_example}

使用如下的代码来测试这个自旋锁

\begin{lstlisting}
void TestSpinLock() {
    SpinLock spinlock;
    std::thread t1([&spinlock]() {
        spinlock.lock();
        for (int i = 0; i < 3; i++) {
            std::cout << "*";
        }
        std::cout << std::endl;
        spinlock.unlock();
    });
    std::thread t2([&spinlock]() {
        spinlock.lock();
        for (int i = 0; i < 3; i++) {
            std::cout << "?";
        }
        std::cout << std::endl;
        spinlock.unlock();
    });
    t1.join();
    t2.join();
}
\end{lstlisting}

控制台打印为

\begin{lstlisting}
***
???
\end{lstlisting}

\subsection{MESI一致性协议}
\textbf{如何保证数据一致性}？这就要提及\textbf{MESI一致性协议}。

MESI 协议，是一种叫作写失效(Write Invalidate)的协议。在写失效协议里，只有一个 CPU 核心负责写入数据，其他的核心，只是同步读取到这个写入。在这个 CPU 核心写入 cache 之后，它会去广播一个"失效"请求告诉所有其他的 CPU 核心。

MESI 协议对应的四个不同的标记

\begin{enumerate}
    \item M：代表已修改(Modified)
    \item E：代表独占(Exclusive)
    \item S：代表共享(Shared)
    \item I：代表已失效(Invalidated)
\end{enumerate}

"已修改"用来告诉其他cpu已经修改完成，其他cpu可以向cache中写入数据。

"独占"表示数据只是加载到当前 CPU核 的store buffer中，其他的 CPU 核，并没有加载对应的数据到自己的 store buffer 里。

这个时候，如果要向独占的 store buffer 写入数据，我们可以自由地写入数据，而不需要告知其他 CPU 核。

那么对应的，共享状态就是在多核中同时加载了同一份数据。所以在共享状态下想要修改数据要先向所有的其他 CPU 核心广播一个请求，要求先把其他 CPU 核心里面的 cache ，都变成无效的状态，然后再更新当前 cache 里面的数据。

我们可以这么理解，如果变量a此刻在各个cpu的StoreBuffer中，那么CPU1核修改这个a的值，放入cache时通知其他CPU核写失效，因为同一时刻仅有一个CPU核可以写数据，但是其他CPU核是可以读数据的，那么其他核读到的数据可能是CPU1核修改之前的。这就涉及我们提到的改动序列了。

\subsection{宽松内存序memory\_order\_relaxed}
因为内存序是为了实现改动序列的，所以为了理解内存序还要结合改动序列讲起。

我们先看一个CPU和内存结构图

%\begin{figure}[h]\centering
%    \label{四核CPU与内存结构图}
%    \includegraphics[width=0.8\textwidth]{四核CPU与内存结构图.jpg}
%\end{figure}
\begin{figure}[h]\centering
	\label{四核CPU与内存结构图}
	\caption{四核CPU与内存结构图}
	\begin{tikzpicture}[node distance=0.7cm]
		\tikzset{
			red_rec/.style={draw, rectangle, fill=red!20, minimum width=3cm, minimum height=1cm},
			yellow_rect/.style={draw, rectangle, fill=yellow!20, minimum width=3cm, minimum height=1cm},
			green_rect/.style={draw, rectangle, fill=green!20, minimum width=7cm, minimum height=1cm},
			blue_rect/.style={draw, rectangle, fill=blue!20, minimum width=15cm, minimum height=1cm},
		}
		
		\pgfmathsetmacro{\firstRow}{5}
		\pgfmathsetmacro{\secondRow}{3.5}
		\pgfmathsetmacro{\thirdRow}{2}
		
		\node[red_rec] (box) at (0, \firstRow) {CPU1};
		\node[red_rec] (box) at (4, \firstRow) {CPU2};
		\node[red_rec] (box) at (8, \firstRow) {CPU2};
		\node[red_rec] (box) at (12, \firstRow) {CPU2};
		
		\node[yellow_rect] (box) at (0, \secondRow) {StoreBuffer};
		\node[yellow_rect] (box) at (4, \secondRow) {StoreBuffer};
		\node[yellow_rect] (box) at (8, \secondRow) {StoreBuffer};
		\node[yellow_rect] (box) at (12, \secondRow) {StoreBuffer};
		
		\node[green_rect] (box) at (2, \thirdRow) {Cache};
		\node[green_rect] (box) at (10, \thirdRow) {Cache};
		
		\node[blue_rect] (box) at (6, 0) {Memory};
		
		\draw[dash pattern=on 4pt off 2pt] (-2+0.25, 1+0.25) rectangle (6-0.25, 6-0.25);
		\draw[dash pattern=on 4pt off 2pt] (6+7.5+0.25, 1+0.25) rectangle (6+0.25, 6-0.25);
		\node at (-2.5, \secondRow) {bank};
		\node at (6+7.5+1, \secondRow) {bank};
	\end{tikzpicture}
\end{figure}

其中StoreBuffer就是一级Cache， Cache是二级Cache，Memory是三级Cache。每个标识CPU的块就是core，上图画的就是4核结构。每两个core构成一个bank，共享一个cache。四个core共享memory。每个CPU所作的store均会写到store buffer中，每个CPU会在任何时刻将store buffer中结果写入到cache或者memory中。

\subsubsection{同步与先行}

\begin{enumerate}
    \item 同步(synchronizes-with)：A synchronizes-with B 的意思就是 A和B同步，简单来说如果多线程环境下，有一个线程先修改了变量m，我们将这个操作叫做A，之后有另一个线程读取变量m，我们将这个操作叫做B，那么B一定读取A修改m之后的最新值。也可以称作 A happens-before B，即A操作的结果对B操作可见。
    \item 先行(happens-before)：A happens-before B 的意思是如果A操作先于B操作，那么A操作的结果对B操作可见。happens-before包含很多种境况，不仅仅是我们上面提到的synchronizes-with,之后给大家一个脑图详细说明happens-before的几种情况。
\end{enumerate}

\subsubsection{再论宽松内存序}

\begin{enumerate}
    \item 作用于原子变量
    \item 不具有synchronizes-with关系
    \item 对于同一个原子变量，在同一个线程中具有happens-before关系, 在同一线程中不同的原子变量不具有happens-before关系，可以乱序执行。
    \item 多线程情况下不具有happens-before关系。
\end{enumerate}

由上述可知，如果采用最松散的内存顺序模型，在一个线程中，如果某个表达式已经看到原子变量的某个值a，则该表达式的后续表达式只能看到a或者比a更新的值。

\subsubsection{代码示例}
\begin{code_example}\label{宽松内存序代码示例1}
\begin{lstlisting}
std::atomic<bool> x, y;
std::atomic<int> z;
void write_x_then_y() {
    x.store(true, std::memory_order_relaxed);  // 1
    y.store(true, std::memory_order_relaxed);  // 2
}
void read_y_then_x() {
    while (!y.load(std::memory_order_relaxed)) { // 3
        std::cout << "y load false" << std::endl;
    }
    if (x.load(std::memory_order_relaxed)) { //4
        ++z;
    }
}

// 测试函数如下
void TestOrderRelaxed() {
    std::thread t1(write_x_then_y);
    std::thread t2(read_y_then_x);
    t1.join();
    t2.join();
    assert(z.load() != 0); // 5
}    
\end{lstlisting}
\end{code_example}

\textbf{从CPU角度分析}

假设线程t1运行在CPU1上，t2运行在CPU3上，那么t1对x和y的操作，t2是看不到的。

比如当线程t1运行至1处将x设置为true，t1运行至2处将y设置为true。这些操作仅在CPU1的store buffer中，还未放入cache和memory中，CPU2自然不知道。

如果CPU1先将y放入memory，那么CPU2就会读取y的值为true。那么t2就会运行至3处从while循环退出，进而运行至4处，此时CPU1还未将x的值写入memory，

t2读取的x值为false，进而线程t2运行结束，然后CPU1将x写入true, t1结束运行，最后主线程运行至5处，因为z为0,所以触发断言。

\textbf{从宽松内存序分析}

因为\verb|memory_order_relaxed|是宽松的内存序列，它只保证操作的原子性，并不能保证多个变量之间的顺序性，也不能保证同一个变量在不同线程之间的可见顺序。

比如t1可能先运行2处代码再运行1处代码，因为我们的代码会被编排成机器语言的指令执行，编译器在不破坏语义的情况下(2处和1处代码无耦合，可调整顺序)，2可能先于1执行。如果这样，t2运行至3处退出while循环，继续运行4处，此时t1还未执行1初代码，则t2运行4处条件不成立不会对z做增加，t2结束。这样也会导致z为0引发断言。

\subsection{顺序一致内存序memory\_order\_seq\_cst}
\verb|memory_order_seq_cst|代表顺序一致内存序，可以用于\verb|store|，\verb|load|和RMW操作(read-modify-write)，实现sequencial consistent内存序模型。在这个模型下, 所有线程看到的所有操作都有一个一致的顺序, 即使这些操作可能针对不同的变量, 运行在不同的线程。

回到代码示例\ref{宽松内存序代码示例1}中，如果将x和y的\verb|store|操作的内存序参数更改为\verb|std::memory_order_seq_cst|，则不会触发断言\verb|assert(z.load() != 0);|

当线程t2执行到3处并退出循环时我们可以断定y为true，因为是全局一致性顺序，所以线程t1已经执行完2处将y设置为true，那么线程t1也一定执行完1处代码并对t2可见，所以当t2执行至4处时x为true，那么会执行z++保证z不为零，从而不会触发断言。

\subsubsection{代价}
实现 sequencial consistent 模型有一定的开销. 现代 CPU 通常有多核, 每个核心还有自己的缓存. 为了做到全局顺序一致, 每次写入操作都必须同步给其他核心. 为了减少性能开销, 如果不需要全局顺序一致, 我们应该考虑使用更加宽松的顺序模型.

\subsection{acquire-release ordering内存序模型}
在 acquire-release 模型中, 会使用 \verb|memory_order_acquire|, \verb|memory_order_release| 和 \verb|memory_order_acq_rel| 这三种内存顺序

\begin{itemize}
    \item 对原子变量的 \verb|load| 可以使用 \verb|memory_order_acquire| 内存顺序，这称为acquire操作
    \item 对原子变量的 \verb|store| 可以使用 \verb|memory_order_release| 内存顺序，这称为release 操作
    \item \verb|read-modify-write| 操作即读 (load) 又写 (store)，如果使用 \verb|memory_order_acquire|, 则作为 acquire 操作，如果使用 \verb|memory_order_release|, 则作为 release 操作，如果使用 \verb|memory_order_acq_rel|, 则同时为两者
\end{itemize}

\subsubsection{重要性}
到此大家一定要记住仅 Acquire-release能配合达到 synchronizes-with效果，再就是\verb|memory_order_seq_cst|可以保证全局顺序唯一，其他情况的内存顺序都能保证顺序，使用时需注意。

Acquire-release 的开销比 sequencial consistent 小. 在 x86 架构下, \verb|memory_order_acquire| 和 \verb|memory_order_release| 的操作不会产生任何其他的指令, 只会影响编译器的优化: 任何指令都不能重排到 acquire 操作的前面, 且不能重排到 release 操作的后面; 否则会违反 acquire-release 的语义. 因此很多需要实现 synchronizes-with 关系的场景都会使用 acquire-release.

\subsection{释放内存序memory\_order\_release}
多个线程对同一个变量release操作，另一个线程对这个变量acquire，那么只有一个线程的release操作和这个acquire线程构成同步关系

\subsection{(C++11)std::atomic\_thread\_fence}
fence可以和原子操作组合进行同步，也可以fence之间进行同步，fence不光可以不依赖原子操作进行同步，而且相比较于同样memory order的原子操作。

\subsubsection{分类}
\begin{enumerate}
    \item release fence
\begin{lstlisting}
std::atomic_thread_fence(memory_order_release)
\end{lstlisting}
    \item acquire fence
\begin{lstlisting}
std::atomic_thread_fence(memory_order_acquire)
std::atomic_thread_fence(memory_order_consume)
\end{lstlisting}
    \item full fence
\begin{lstlisting}
std::atomic_thread_fence(memory_order_acq_rel)
std::atomic_thread_fence(memory_order_seq_cst)
\end{lstlisting}
\end{enumerate}

注意，\verb|std::atomic_thread_fence(memory_order_relaxed)|是没有任何效果的。

\subsubsection{release fence}
Release fence可以防止fence前的内存操作重排到fence后的任意store之后，即阻止loadstore重排和storestore重排。

\subsubsection{acquire fence}
acquire fence可以防止fence后的内存操作重排到fence前的任意load之前，即阻止loadload重排和loadstore重排

\subsubsection{full fence}
full fence是release fence和acquire fence的组合。

防止loadload、loadstore、storestore重排

\section{(C++20)信号量semaphore}
\subsection{信号量的概念}
信号量是并发机制中解决同步问题的一种技术路线。通过控制线程的并发数量来制约对共享资源的并发访问。

信号量比条件变量的效率更高(by cppreference.com)

\paragraph{信号量的作用}
对于会产生竞争的数据，必须控制多个线程对他的访问，也就是说，不能有过多的线程同时对他进行访问(这里的访问包括读写两个方向)。

假如有某个数据，它最多只支持3个线程同时对他进行访问，则可以将他的信号量设置为3。程序开始运行之后，每有一个线程已经开始访问该数据，则信号量就-1；每有一个线程结束访问、交还所有权，则信号量就+1。假如有某个线程想要访问该数据，但是此时信号量为0，则这个访问是被拒绝的，线程先被挂起，等待有其他的线程交还了访问控制权，信号量从0变为1，则这个等待的线程获得了这个新增加的访问控制权，因此就可以对数据进行访问。

对信号量的操作，都是原子操作。

\paragraph{与互斥锁的关系}
其实，互斥锁本身就是一个计数为1的信号量，因为同一时刻只有一个线程可以获得对产生数据竞争的数据的访问权。

互斥锁相比信号量增加了所有权的概念，一只锁住的互斥锁只能由给它上锁的线程解开，只有系铃人才能解铃。互斥锁的功能也就因而限制在了构造临界区上。

而信号量可以由任一线程"解锁"(或者说，获取访问控制权)。

\subsection{(C++20)semaphore}
C++20提供了库\verb|<semaphore>|来实现信号量

\verb|<semaphore>|库提供了2种信号量
\begin{enumerate}
\item \verb|std::counting_semaphore|可以实现非负资源计数的信号量
\item \verb|std::binary_semaphore|仅拥有二个状态的信号量
\end{enumerate}

\verb|std::binary_semaphore|实质上是\verb|std::counting_semaphore|的模板特化

相当于\verb|using binary_semaphore = std::counting_semaphore<1>;|。

\subsection{使用信号量}
\paragraph{声明信号量}
下面的代码示例中，这里的6，说明这个信号量有6次计数，与之相对的是\verb|std::binary_semaphore|只有2个计数。
\begin{lstlisting}
std::counting_semaphre<6> test{0};
\end{lstlisting}

\verb|std::binary_semaphore test;|

\paragraph{使用}
信号量是一个整数count，提供两个原子操作(atom，不可分割)：P操作和V操作，或者称之为wait和signal操作。

\begin{enumerate}
\item \verb|acquire()|方法，P操作(wait操作)：count减1；如果count < 0 那么挂起当前线程
\item \verb|release()|方法，V操作(signal操作)：count加1；如果count <= 0 那么唤醒一个正在等待的线程
\end{enumerate}

\verb|release()|方法可以带参数，比如\verb|release(2)|，那么计数量则会加2

相比之下，条件变量每次操作只能唤醒1个或者所有线程，使用起来不如信号量灵活。

\paragraph{其他函数}
\begin{enumerate}
\item \verb|try_acquire()|尝试减少内部计数器而不阻塞
\item \verb|try_acquire_for()| 尝试减少内部计数器，至多阻塞一段时长
\item \verb|try_acquire_until()| 尝试减少内部计数器，阻塞直至一个时间点常量
\item \verb|max()| 返回内部计数器的最大可能值
\end{enumerate}

\section{异步模型}
\subsection{future和promise}
future和promise是一种非阻塞模型，也是一种异步模型。

在公共资源未完成计算时取获取其值，是不安全、不正确的行为。

\verb|future|和\verb|promise|类可以负责线程间通信。其基本原理是，把\verb|promise|传递给子线程，从\verb|future|中拿到子线程经过\verb|promise|返回的结果。

\subsubsection{用烧茶水理解异步模型}
烧茶水有三步：烧开水、买茶叶、泡茶叶。为节约时间，烧开水可以和买茶叶同时进行，泡茶叶必须在前两者完成后才能进行。

许多场景都可以抽象成烧茶水这样的异步模型，比如：

IO请求。cpu拉起io任务后，继续做手上的工作，io任务完成后，修改某个信号，cpu每执行一条指令都查看该信号是否改变，若检测到改变，再做响应后处理。

\subsubsection{future和promise的含义}
future表示一个可能还没有实际完成的\textbf{异步任务的结果}，针对这个结果可以添加回调函数以便在任务执行成功或失败后做出对应的操作(回调就是自己写了却不调用，给别人调用的函数)

promise交由任务执行者，任务执行者\textbf{通过promise可以标记任务完成或者失败}

所以说，future/promise编程模型本质上还是message pass(任务线程与主线程消息传递)。在future模型中阻塞和非阻塞都有：拉起一个新线程(非阻塞)，在主线程.get()(阻塞)

\subsubsection{如何获得返回值}
通过future，future可以看成存储器，存储一个未来返回值

先在主线程内创建一个promise对象，从promise对象中获得future对象；

再将promise引用传递给任务线程(举例\verb|std::thread t(print_int, std::ref(fut));|)，在任务线程中对promise进行\verb|set_value|，主线程可通过future获得结果

\subsection{使用future/promise}
\subsubsection{声明}
需要写明要传递的数据类型，通过模板加载，\verb|future|亦如是。

\begin{lstlisting}
std::promise<int> p;
std::future<int> f;
\end{lstlisting}

\subsubsection{绑定}
通过\verb|get_future()|将\verb|future|和\verb|promise|进行绑定。

\begin{lstlisting}
f = p.get_future();
\end{lstlisting}

如果\verb|promise|被释放了，而其他的线程还未使用与\verb|promise|关联的\verb|future|，当其使用这个\verb|future|时会报错。

可以看下面的代码示例

\begin{lstlisting}
// 博主: 恋恋风辰2017
void use_promise_destruct() {
    std::thread t;
    std::future<int> fut;
    {
        // 创建一个 promise 对象
        std::promise<int> prom;
        // 获取与 promise 相关联的 future 对象
        fut = prom.get_future();
        // 在新线程中设置 promise 的值
        t = std::thread(set_value, std::move(prom));
    }
    // 在主线程中获取 future 的值
    std::cout << "Waiting for the thread to set the value...\n";
    std::cout << "Value set by the thread: " << fut.get() << '\n';
    t.join();
}
\end{lstlisting}

\subsubsection{传递给线程}
\verb|std::future|传递给线程，一般以引用的方式进行。

\verb|std::future|删除了拷贝构造函数，和拷贝赋值运算符。
\begin{lstlisting}
void thread_function(std::future<int>& fut);
    
std::future<int> fut = prom.get_future();
std::thread t(thread_function, std::ref(fut));
\end{lstlisting}

\verb|std::promise|传递给线程，一般以移动的方式。

\verb|std::promise|删除了拷贝构造函数和拷贝赋值运算符，但是提供了移动构造函数和移动赋值运算符。
\begin{lstlisting}
std::future<int> fut;
std::promise<int> prom;
fut = prom.get_future();
std::thread(set_value, std::move(prom));
\end{lstlisting}

\subsubsection{传值 - get()与wait()}
\verb|std::future::get()|是一个阻塞调用，用于获取\verb|std::future|对象表示的值或异常。如果异步任务还没有完成，\verb|get()|会阻塞当前线程，直到任务完成。如果任务已经完成，\verb|get()|会立即返回任务的结果。

\verb|get()|\textbf{只能调用一次}，因为它会移动或消耗掉\verb|std::future|对象的状态。一旦 \verb|get()| 被调用，\verb|std::future| 对象就不能再被用来获取结果。

\verb|std::future::wait()| 也是一个阻塞调用，但它与 \verb|get()| 的主要区别在于 \verb|wait()| 不会返回任务的结果。它只是等待异步任务完成。如果任务已经完成，\verb|wait()| 会立即返回。如果任务还没有完成，\verb|wait()| 会阻塞当前线程，直到任务完成。与 \verb|get()| 不同，\verb|wait()| 可以被多次调用，它不会消耗掉 \verb|std::future| 对象的状态。

\verb|get()|方法本质上就相当于先调用\verb|wait()|然后等待子线程返回期望的值。

在子线程中，使用\verb|set_value()|方法来赋值，就可以在外面通过\verb|std::future::get()|得到值。

可以使用\verb|std::future|的\verb|wait_for()|或\verb|wait_until()|方法来检查异步操作是否已完成。这些方法返回一个表示操作状态的\verb|std::future_status|值。

\begin{lstlisting}
// 博主: 恋恋风辰2017
if(fut.wait_for(std::chrono::seconds(0)) == std::future_status::ready) {  
    // 操作已完成  
} else {  
    // 操作尚未完成  
}
\end{lstlisting}

\subsubsection{复制}
\verb|future|可以复制，给多个线程使用。

\verb|promise|不能复制，不可以使用\verb|p1 = p2;|的形式来给新的变量赋值。

但是可以用\verb|std::move()|来进行移动，例如\verb|std::promise<int> p2 = std::move(p1);|

可以看下面的代码示例

\begin{lstlisting}
#include <iostream>       
#include <functional>     
#include <thread>        
#include <future>     // std::promise, std::future

void print_int(std::future<int>& fut) {
    int x = fut.get();                    // 获取共享状态的值.
    std::cout << "value: " << x << '\n';  // 打印 value: 10.
}

int main ()
{
    std::promise<int> prom;                    // 生成一个 std::promise<int> 对象.
    std::future<int> fut = prom.get_future();  // 和 future 关联.
    std::thread t(print_int, std::ref(fut));   // 将 future 交给另外一个线程t.
    prom.set_value(10);                        // 设置共享状态的值, 此处和线程t保持同步.
    t.join();
    return 0;
}
\end{lstlisting}

\begin{code_example}\label{promise代码示例1}
\begin{lstlisting}
// 博主: 恋恋风辰2017
#include <iostream>
#include <thread>
#include <future>
void set_value(std::promise<int> prom) {
    // 设置 promise 的值
    prom.set_value(10);
}
int main() {
    // 创建一个 promise 对象
    std::promise<int> prom;
    // 获取与 promise 相关联的 future 对象
    std::future<int> fut = prom.get_future();
    // 在新线程中设置 promise 的值
    std::thread t(set_value, std::move(prom));
    // 在主线程中获取 future 的值
    std::cout << "Waiting for the thread to set the value...\n";
    std::cout << "Value set by the thread: " << fut.get() << '\n';
    t.join();
    return 0;
}
\end{lstlisting}
\end{code_example}

\subsubsection{传递异常}
\verb|std::promise|除了能够传递值，还可以传递异常，使用的是\verb|set_exception()|方法。

该方法接受一个\verb|std::exception_ptr|参数，该参数可以通过调用\verb|std::current_exception()|方法获取。

\begin{code_example}\label{promise代码示例2}
\begin{lstlisting}
// 博主: 恋恋风辰2017
#include <iostream>
#include <thread>
#include <future>
void set_exception(std::promise<void> prom) {
    try {
        // 抛出一个异常
        throw std::runtime_error("An error occurred!");
    } catch(...) {
        // 设置 promise 的异常
        prom.set_exception(std::current_exception());
    }
}
int main() {
    // 创建一个 promise 对象
    std::promise<void> prom;
    // 获取与 promise 相关联的 future 对象
    std::future<void> fut = prom.get_future();
    // 在新线程中设置 promise 的异常
    std::thread t(set_exception, std::move(prom));
    // 在主线程中获取 future 的异常
    try {
        std::cout << "Waiting for the thread to set the exception...\n";
        fut.get();
    } catch(const std::exception& e) {
        std::cout << "Exception set by the thread: " << e.what() << '\n';
    }
    t.join();
    return 0;
}    
\end{lstlisting}
\end{code_example}

代码输出为：
\begin{lstlisting}
Waiting for the thread to set the exception...
Exception set by the thread: An error occurred!
\end{lstlisting}

\subsubsection{共享的future}
当我们需要多个线程等待同一个执行结果时，需要使用\verb|std::shared_future|。

\verb|std::shared_future|既支持移动也支持拷贝，而\verb|std::future|是不能拷贝的。

在代码示例\ref{共享的future代码示例1}中，创建了1个\verb|std::promise<int>|对象\verb|promise|，和1个与之关联的\verb|std::shared_future<int>|对象\verb|future|。

有线程\verb|myThread1|执行任务，并将\verb|promise|移动进去设置其值。

有2个线程\verb|myThread2|和\verb|myThread3|等待\verb|future|的结果。

如果\verb|myThread1|中成功设置了\verb|promise|的值，则\verb|myThread2|和\verb|myThread3|这2个线程将同时通过\verb|future|得到所需要的结果。

注意\verb|promise|传递给线程，使用的是移动的方式(\verb|std::move|)。而\verb|future|并不是。

\begin{code_example}\label{共享的future代码示例1}
\begin{lstlisting}
// 博主: 恋恋风辰2017
void myFunction(std::promise<int>&& promise) {
    // 模拟一些工作
    std::this_thread::sleep_for(std::chrono::seconds(1));
    promise.set_value(42); // 设置 promise 的值
}
void threadFunction(std::shared_future<int> future) {
    try {
        int result = future.get();
        std::cout << "Result: " << result << std::endl;
    }
    catch (const std::future_error& e) {
        std::cout << "Future error: " << e.what() << std::endl;
    }
}
void use_shared_future() {
    std::promise<int> promise;
    std::shared_future<int> future = promise.get_future();
    std::thread myThread1(myFunction, std::move(promise)); // 将 promise 移动到线程中
    // 使用 share() 方法获取新的 shared_future 对象  
    std::thread myThread2(threadFunction, future);
    std::thread myThread3(threadFunction, future);
    myThread1.join();
    myThread2.join();
    myThread3.join();
}    
\end{lstlisting}
\end{code_example}

\subsection{std::pakcage\_task}
\subsubsection{功能}
\verb|std::pakcage_task|将可调用对象包装起来，创建一个异步任务，将调用结果返回给\verb|std::future|对象。

\verb|std::pakcage_task|不支持拷贝操作，但是支持移动。

\subsubsection{与future连用}
\begin{enumerate}
    \item 创建一个\verb|std::packaged_task|对象，该对象包装了要执行的任务。
    \item 调用\verb|std::packaged_task|对象的\verb|get_future()|方法，该方法返回一个与任务关联的\verb|std::future|对象。
    \item 在另一个线程上调用\verb|std::packaged_task|对象的\verb|operator()|，以执行任务。
    \item 在需要任务结果的地方，调用与任务关联的\verb|std::future|对象的\verb|get()|方法，以获取任务的返回值或异常。
\end{enumerate}

\paragraph{代码示例}
创建了一个包装了任务的\verb|std::packaged_task|对象，并获取了与任务关联的\verb|std::future|对象。然后，我们在另一个线程上执行任务，并等待任务完成并获取结果。最后，输出结果。

\begin{code_example}\label{package_task代码示例2}
\begin{lstlisting}
// 博主: 恋恋风辰2017
int my_task() {
    std::this_thread::sleep_for(std::chrono::seconds(5));
    std::cout << "my task run 5 s" << std::endl;
    return 42;
}
void use_package() {
    // 创建一个包装了任务的 std::packaged_task 对象  
    std::packaged_task<int()> task(my_task);
    // 获取与任务关联的 std::future 对象  
    std::future<int> result = task.get_future();
    // 在另一个线程上执行任务  
    std::thread t(std::move(task));
    t.detach(); // 将线程与主线程分离，以便主线程可以等待任务完成  
    // 等待任务完成并获取结果  
    int value = result.get();
    std::cout << "The result is: " << value << std::endl;
}
\end{lstlisting}
\end{code_example}

\paragraph{代码示例}
假设有一个函数\verb|int thread_func(int para);|

使用\verb|std::pakcage_task|进行包装\verb|std::package_task<int(int)> pkt(thread_func)|，这里\verb|int(int)|的含义是一个"传入类型为\verb|int|返回类型为\verb|int|"的可调用对象。

拉起线程，或者不新建线程，只调用线程入口函数，都可以异步地运行这个函数。

\begin{code_example}\label{package_task代码示例1}
\begin{lstlisting}
int thread_func(int para);
std::package_task<int(int)> pkt(thread_func);

// 用法1：
std::thread t1(std::ref(pkt), para); // 创建线程 -- 注意要用引用
t1.join(); // 主线程等待
// 用法2： -- 实际上相当于调用函数，没有开始新线程
pkt(para); // 调用入口函数
// 就像调用了thread_func(para);一样    
\end{lstlisting}
\end{code_example}

获取值的方法：

\begin{lstlisting}
std::future<int> fu = pkt.get_future(); // 得到返回值
int res = fu.get();
\end{lstlisting}

可以看出，\verb|std:packaged_task|需要显式的调用或者传递给\verb|std::thread|进行异步调用

需要注意的是，一定要在调用\verb|f.get()|之前，执行了\verb|std::packaged_task|，否则你的程序会一直阻塞在那

\subsection{std::async}
\verb|std::async|是一个接受回调(函数或函数对象)作为参数的模板函数，并异步执行它。

\verb|std::async|返回一个\verb|std::future<T>|，它存储由\verb|std::async()|执行的函数对象返回的值。

当需要最终结果时，调用\verb|std::future::get()|方法即可，该方法包含阻塞操作，会阻塞线程直到期望值状态就绪为止。可以看下面的代码示例

\begin{lstlisting}
#include <future>
#include <iostream>
int find_the_answer_to_ltuae();
void do_other_stuff();

int main()
{
    std::future<int> the_answer=std::async(find_the_answer_to_ltuae);
    do_other_stuff();
    std::cout << "The answer is " << the_answer.get() << std::endl;
}
\end{lstlisting}

\begin{lstlisting}
// 博主: 恋恋风辰2017
#include <iostream>
#include <future>
#include <chrono>
// 定义一个异步任务
std::string fetchDataFromDB(std::string query) {
    // 模拟一个异步任务，比如从数据库中获取数据
    std::this_thread::sleep_for(std::chrono::seconds(5));
    return "Data: " + query;
}
int main() {
    // 使用 std::async 异步调用 fetchDataFromDB
    std::future<std::string> resultFromDB = std::async(std::launch::async, fetchDataFromDB, "Data");
    // 在主线程中做其他事情
    std::cout << "Doing something else..." << std::endl;
    // 从 future 对象中获取数据
    std::string dbData = resultFromDB.get();
    std::cout << dbData << std::endl;
    return 0;
}
\end{lstlisting}

\subsubsection{启动策略}
\verb|std::launch|中两个标志位会影响\verb|std::async()|实现方式

\begin{enumerate}
    \item \verb|std::launch::async|会立刻创建异步线程开始运行。
    \item \verb|std::launch::deferred|则会不会立刻创建异步线程，而是推迟到\verb|get()|时才运行。
    \item \verb/|std::launch::async | std::launch::deferred|/，默认情况下系统会采用这种策略，取决于具体编译器的实现方式。
\end{enumerate}

\subsubsection{惰性求值(lazy evaluation)}
\verb|std::async|的第一个参数可以设置为\verb|std::launch::deferred|，这样不会创建线程，只会把lambda函数体里面的运算推迟到\verb|future|的\verb|get()|被调用时。

创建延时任务，\verb|future<int> result = async(launch::deferred, mythread);|

\verb|int res = result.get();|

\section{非阻塞并发(non-blocking)}
并发编程，从阻塞发生程度由高到低，可以分为以下几类
\begin{enumerate}
    \item 阻塞并发(blocking)：阻塞算法就是指可以让线程休眠直到共享资源可用。这个算法会有死锁活锁饥饿和优先级倒置的问题。
    \item 无阻塞并发(obstruction free)：指在任何时间点，一个孤立运行线程的每一个操作可以在有限步之内结束。只要没有竞争，线程就可以持续运行，一旦共享数据被修改，Obstruction-free 要求中止已经完成的部分操作，并进行回滚，obstruction-free 是并发级别更低的非阻塞并发，该算法在不出现冲突性操作的情况下提供单线程式的执行进度保证，所有 Lock-Free 的算法都是 Obstruction-free 的。
    \item 无锁并发(lock free)：保证系统中的线程都能运行，也就是说如果有一条线程被挂起，其他线程也可以运行。这导致和锁冲突，因为一个持有锁的线程如果休眠的话，会导致其他线程也阻塞。无锁并发算法是基于事务的算法，如果一个事务被其他线程打断失败了，那么会回滚重试。这个技术可避免死锁，但是会导致饥饿，某条线程可能一直重试失败。
    \item 无等待并发(wait free)：无等待并发不仅能够无锁，还能保证无饥饿，所有线程可以运行。
\end{enumerate}

\subsection{实现线程安全}
线程安全的队列，数组，链表，哈希表

\subsection{无锁技术}
无锁栈，无锁队列

M for multi, S for single
\begin{enumerate}
    \item SPSC：单个生产者对单个消费者(无等待、有界和无界都有实现)
    \item MPSC：多个生产者对单个消费者(无锁、有界和无界都有实现)
    \item SPMC：单生产者对多个消费者(无锁 有界)
    \item MPMC：多生产者对多个消费者(无锁、有界)
\end{enumerate}

多线程协调的问题，最后会回到ReaderWriter问题，然后进一步分成了，SPSC，SPMC，MPSC，MPMC这四个。

SPSC 没有背压，生产消费速度差不多，RingBuffer就解决了，CAS 都不用。

SPMC这个其实就是一个Topic下面一堆消费者(Kafka？)那就是解决分派就可以了，大部分时候生产没有消费快，所以上锁也无所谓，因为主要靠Monitor来解决唤醒这个事儿。

MPSC，这个场景很有可能出现背压，那么就需要反向通知P。

MPMC这个玩意呢，PC都有可能被Block，也需要唤醒，这个结构有没有锁都可以实现。

\subsubsection{背压机制(back pressure)}
背压机制，实际上是流控制(flow control)的其中一种解决方案。

流控制还包含哪些技术呢？
\begin{enumerate}
    \item 背压
    \item 节流(throttling)，处理不了的数据就直接丢弃。这里有可思考的地方就是，丢弃哪些数据。
    \item 打包(buffer或者window)，将上游的多个小包裹打包，分发给下游。
    \item 调用阻塞(callback blocking)，整个流程就停止了，好比是单行线而且只有单车道，那么前面的车如果停下了，后面的车也只能等待。
\end{enumerate}

\section{(C++20/23)协程}\label{(C++20/23)协程}
\subsection{认识协程}
\subsubsection{协程的经典文章}
\href{https://lewissbaker.github.io/}{https://lewissbaker.github.io/}lewissbaker的文章，是C++协程思想的很经典文章。

\subsubsection{协程与函数}
从普通函数的角度，来引出协程的思想。协程概念不是凭空产生的，是从生产实践中总结归纳出来的。

普通的函数，当一个函数调用另一个函数的时候会发生什么？调用者，先把自己的所有数据保存入调用栈里面，被调用者在结束函数后(遇到\verb|return|语句)，会回到调用发生的时候调用者正在运行的情况下。这个保存上下文的过程，在计算机原理中已经详细论述过了。

协程，实际上，与这种调用过程具有相似性，就是协程也有调用，返回这两个过程，而且协程也存在保存上下文这一过程，但是区别是它并不将上下文保存在调用栈中(具体保存在哪里这里暂略)，总之是有保存的过程，并且在运行结束后还会将之读取出来。

我们把函数的调用过程，抽象为调用(call)和返回(return)这两个概念。协程，我们将他抽象出3个概念，暂停(suspend)，恢复(resume)和销毁(destroy)。

协程与函数的其中一个区别是，他可以在用户指定的任意位置处返回到其调用者，这个过程被称为暂停(suspend)。而且还可以在用户指定的时候恢复到原来暂停的位置，这个过程就是恢复(resume)。协程只有在完全运行完之后，才会回收所有他所使用的内存资源，这个过程就是销毁(destroy)。

协程在暂停(suspend)和恢复(resume)时，也会保存和读取上下文，但是这个上下文保存的方式与函数不同。并不是保存在栈结构上，而是保存在堆区域中。

函数之所以可以使用栈结构来保存其调用的上下文，是因为函数之间有十分严格的调用关系，调用和\verb|return|语句是严格成对出现的。这里并未讨论异常(exception)的情况。

而协程提供了更自由的选择，他可以在任何一个程序员想要的位置回到其调用者，又可以在任意时间重新回到协程中。这种灵活性，就使得栈结构无法满足这种自由度了。所以协程的上下文，保存在堆区域，他保存的内容仍然是类似于上下文的那些信息。我们可以姑且把他称为协程帧(coroutine frame)，但是要注意他不是调用栈那样的栈结构。

\subsubsection{协程与线程的关系}
线程是操作系统调度的最小单位，线程的切换是操作系统来执行的，所以他一定会发生用户态到内核态的切换，这就带来了成本。虽然，线程的切换成本已经比进程的切换要小得多了。

如果把协程的上下文都保存在用户空间，这样就避免了进入内核态带来的成本，同时也实现了控制流的并发，在一段时间内同时执行多个任务。

所以，协程，可以理解成为用户态的线程。

协程更加适用于IO密集型的任务中，因为IO通常会伴随着大量的阻塞等待过程，而使用协程就可以在IO阻塞的同时让出CPU，而当IO就绪后再主动抢占CPU即可。

即使你创建了N多协程，但在操作系统看来依然只有一个线程，也就是说协程对操作系统来说是不可见的。操作系统只知道这里有1个线程在一直运行，至于这个线程上运行了多少个协程，操作系统不知道，也无需关心。因为用户态的工作做得很到位，减少了阻塞的发生，操作系统一直高负荷地处于取指令执行指令的过程中。

协程是非抢占式多任务，线程是抢占式多任务。

协程需要编写代码者主动让出控制权，而线程可以无需规划让出控制权的时间点。线程让出控制权的时间点，不应由开发人员干预，他是操作系统的调度算法关心的事情。

实际上，协程的概念出现得比线程要更早。或者说，早期人们对于控制流的实现方式还没有研究得这么深，所以协程和线程概念还没有分化得特别清晰，后来随着高级语言对于实现的逐渐细化，线程、进程、协程这些概念逐渐清晰，在业界内产生共识。

协程只是最近才开始加入C++标准，而并不是最近才在计算机科学中出现，他是很早就出现了的概念。在C++标准添加线程之前，已经有了许多协程的库实现。

\subsubsection{libco库}
\verb|libco|库是腾讯于2013年在GitHub上开源的C++协程库，也是微信底层所使用的协程库。\href{https://link.zhihu.com/?target=https%3A//github.com/Tencent/libco}{libco}

\subsection{C++20中的协程}
首先，C++ 20 协程是一个无栈(stackless)的协程。同时，C++ 20 协程是非对称的协程。

C++20中新增了那些与协程有关的内容
\begin{itemize}
    \item 三个新语言关键字：\verb|co_await|、\verb|co_yield| 和 \verb|co_return|。
    \item \verb|std|命名空间中的几种新类型
        \begin{itemize}
            \item \verb|coroutine_handle<P>|
            \item \verb|coroutine_traits<Ts...>|
            \item \verb|suspend_always|
            \item \verb|suspend_never|
        \end{itemize}
\end{itemize}

\subsubsection{C++20并未给出具体实现，只是给出了基础支持}
C++20协程提供的设施可以看作是协程的低级汇编语言。这些设施很难直接安全地使用，主要供库编写者用来构建高层封装，以便后续的应用程序开发人员可以安全使用的。不过在后续的C++标准中也将提供一些安全的高层封装(比如C++23的\verb|std::generator|)

C++20协程实际上并没有定义协程的语义。它没有定义如何产生返回值给调用者；它没有定义如何处理传递给 \verb|co_return| 语句的返回值；也没有定义如何处理从协程传播出去的异常；甚至也没有定义应在哪个线程上恢复协程。

C++20协程定义了两个接口类：\verb|Promise| 接口类和 \verb|Awaitable| 接口类。

\begin{itemize}
    \item \verb|Promise| 接口类指定了用于自定义协程本身行为的方法。库编写者可以自定义调用协程时发生的情况、协程返回时发生的情况(通过正常方式或未处理异常)，以及自定义协程中任何 \verb|co_await| 或 \verb|co_yield| 表达式的行为。
    
    \item \verb|Awaitable| 接口类指定了一些方法，用来控制 \verb|co_await| 表达式的语义。当一个值被 \verb|co_awaited| 时，代码会被转换成一系列对\verb|Awaitable|对象上的方法的调用，这些方法允许它指定：是否暂停当前协程；在暂停后执行一些逻辑以调度协程的后续恢复；以及在协程恢复后执行一些逻辑以产生 \verb|co_await| 表达式的结果。
\end{itemize}

\subsubsection{co\_await 运算符}
\verb|co_await| 运算符是一个新的一元运算符，可以应用于一个值。例如：\verb|co_await someValue|。\verb|co_await| 运算符只能在协程中使用。这有点像同义反复，因为根据定义，任何包含 \verb|co_await| 运算符的函数体都将被编译为协程。

支持 \verb|co_await| 运算符的类型称为 \verb|Awaitable|

\verb|Awaiter| 类型实现了三个特殊方法：\verb|await_ready|、\verb|await_suspend| 和 \verb|await_resume|，这些方法将作为 \verb|co_await| 表达式的一部分被调用。

一个类型既可以是 \verb|Awaitable| 类型，也可以是 \verb|Awaiter| 类型。

\subsubsection{带有以下内容的函数就称为协程}
一个函数，如果他函数体内部有以下3个关键字，\verb|co_await|、\verb|co_yield| 和 \verb|co_return|，那他成为了协程。

协程实际上是一种函数。

\subsubsection{限制}
协程不能使用可变参数，不能使用普通的\verb|return|语句，或者带有占位符的返回语句(\verb|auto|或者\verb|concept|)。

Consteval functions, 常量表达式函数(constexpr functions), 构造函数，析构函数，主函数，都不能成为协程。

\subsubsection{协程的组成}
协程的执行，主要包含以下细节
\begin{enumerate}
    \item \verb|promise|对象，在内部操作一个协程。协程通过\verb|promise|对象来传递其返回值(或者异常)。注意，这里的\verb|promise|不要与多线程库中的\verb|std::promise|混淆。
    \item 协程句柄(coroutine handle)，在外部操作一个协程。这是一个无所有者的句柄(a non-owning handle)，用于在外部恢复协程执行，或者销毁协程栈。
    \item 协程状态(coroutine state)，是一个内部的动态分配的存储空间。包含以下内容
        \begin{itemize}
            \item \verb|promise|对象
            \item 所有的参数(按值传递复制而来)
            \item 当前中断点的信息(some representation of the current suspension point)，这样可以知道在何处恢复执行，以及销毁时知道哪些局部变量在作用域内。
            \item 在当前中断点仍然处于生命周期内的本地变量和临时变量等。
        \end{itemize}
\end{enumerate}

\subsubsection{协程的执行}
当一个协程开始执行时，他会有如下行为。
\begin{enumerate}
    \item 使用\verb|new|运算符来分配协程状态对象。
    \item 将所有参数，复制到协程状态对象中。按值传递的，进行移动或者复制。按引用传递的，仍然保持引用类型。(这里的引用可能产生悬空，所以要关注原数据和协程之间的生命周期关系)
    \item 调用\verb|promise|对象的构造函数。If the promise type has a constructor that takes all coroutine parameters, that constructor is called, with post-copy coroutine arguments. 其他情况是调用默认构造函数。
    \item 调用\verb|promise.get_return_object()|，将结果保存在一个本地变量中。当协程第一次中断时，这个结果会被返回给调用者，同样，异常也会传播给调用这种。
    \item 调用\verb|promise.initial_suspend()|并且\verb|co_awaits|他的结果。
    
    典型的\verb|promise|类型，要么返回一个\verb|std::suspend_always|，要么返回\verb|std::suspend_never|。
    \item 当\verb|co_await promise.initial_suspend()|恢复执行时，协程的主体开始执行。
\end{enumerate}

\subsubsection{协程遇到暂停点(suspension point)时}
当协程遇到一个暂停点(suspension point)。

事先已得到的返回对象，被返回给调用者(caller)，或者暂停发起者(resumer)，如果需要的话，此时在返回之前会发生相应的隐式类型转换。

\subsubsection{协程遇到co\_return语句时}
当协程遇到一个\verb|co_return|语句。
\begin{enumerate}
    \item 遇到\verb|co_return;|语句，或者遇到\verb|co_return expr;|语句(此处的\verb|expr|的类型是\verb|void|时)，调用\verb|promise.return_void()|。
    \item 遇到\verb|co_return expr;|时，如果\verb|expr|的类型是非空的，那么会调用\verb|promise.return_value(expr)|。
    \item 销毁所有自动存储期(automatic storage duration)的变量，以他们创建时的顺序的相反顺序。
    \item 调用\verb|promise.final_suspend()|并且\verb|co_awaits|其结果。
\end{enumerate}

协程执行到最后时，与遇到\verb|co_return|语句是一样的。此时，如果没有找到\verb|promise.return_void()|的定义，则会产生未定义行为UB。

\subsubsection{协程销毁时}
当协程状态因通过\verb|co_return|或未捕获异常终止而被销毁，或者因通过其句柄被销毁时，它会执行以下操作
\begin{enumerate}
    \item 调用\verb|promise|对象的析构函数。
    \item 曾经传入的座位函数参数的对象(按值传入复制而来的)，执行他们的析构函数。
    \item 调用\verb|delete|运算符以释放协程状态所使用的内存。
    \item 将执行传回给调用者，恢复者。
\end{enumerate}

\subsubsection{动态分配}
协程状态所占用的内存空间，是通过非数组运算符(non-array operator)\verb|new|动态分配的。

如果\verb|Promise|定义了对\verb|new|的类级别的替换，则使用这个替换的，否则使用全局的\verb|new|。

If the Promise type defines a placement form of operator \verb|new| that takes additional parameters, and they match an argument list where the first argument is the size requested (of type \verb|std::size_t|) and the rest are the coroutine function arguments, those arguments will be passed to operator \verb|new| (this makes it possible to use leading-allocator-convention for coroutines).

\subsubsection{promise类型}
\verb|promise|类型，由编译器定义，作为协程的返回类型，用到了\verb|std::coroutine_traits|模板。

\subsubsection{std::coroutine\_traits}
\verb|std::coroutine_traits|是一个模板类，用于定义\verb|promise|的类型。
\begin{lstlisting}
template< class R, class... Args >
struct coroutine_traits;
\end{lstlisting}

\verb|R|表示返回类型。\verb|Args|表示传入参数的类型，他是一个参数包，包含可变数量的类型参数。

根据要返回的类型和要传入的参数的类型，来推断生成\verb|promise|的类型。

标准库实现提供了一个公共可访问的成员类型\verb|promise_type|，与\verb|R::promise_type|相同，前提是限定名合法且能够表示类型。否则，他是不存在这个成员。

\subsubsection{co\_await}
一元运算符\verb|co_await|将会暂停协程，并且将控制权返回给调用者。

他的操作数，可以是下面几种情况
\begin{enumerate}
    \item 是一个类类型，定义了一个\verb|co_await|运算符，或者可以被传递给一个非成员运算符\verb|co_await|。
    \item 或者可以被当前协程通过\verb|Promise::await_transform()|转换成一个符合前面描述的条件的类类型。
\end{enumerate}

\begin{lstlisting}
co_await expr		
\end{lstlisting}

一个\verb|co_await|表达式，只能出现在常规函数的函数体里(包括lambda表达式的函数体部分)的一个潜在求值表达式(potentially-evaluated expression)中。

不能出现在以下情况中
\begin{enumerate}
    \item 异常处理程序中；
    \item 声明语句中，除非它出现在该声明语句的初始化器中；
    \item 简单声明的init-statement(如if、switch、for和range-for)中，除非它出现在该init-statement的初始化器中；
    \item 默认参数中；
    \item 具有静态或线程存储持续时间的块作用域变量的初始化器中。
\end{enumerate}

首先\verb|expr|会被转化为一个可等待对象(awaitable)。

\chapter{池化技术}
\section{线程池}
\subsection{线程池}
线程池是一种多线程处理形式，它处理过程中将任务添加到队列，然后在创建线程后自动启动这些任务。

如果某个线程在托管代码中空闲(如正在等待某个事件),则线程池将插入另一个辅助线程来使所有处理器保持繁忙。如果所有线程池线程都始终保持繁忙，但队列中包含挂起的工作，则线程池将在一段时间后创建另一个辅助线程但线程的数目永远不会超过最大值。

超过最大值的线程可以排队，但他们要等到其他线程完成后才启动。

线程池可以避免在处理短时间任务时创建与销毁线程的代价，它维护着多个线程，等待着监督管理者分配可并发执行的任务，从而提高了整体性能。

\section{对象池}
\paragraph{优点}
节省申请内存空间的系统调用的发生次数，减少穿越用户态进入内核态的发生次数。
\paragraph{分类}
基于数组、堆、栈、区块来实现对象池。

提供一个策略接口，策略接口提供数组、堆、栈、区块四种策略，或者其他策略，来进行资源分配。

\paragraph{策略接口}
策略接口是一个基类，带有2个纯虚函数，由派生类实现。

策略接口函数\verb|allocate()|和\verb|deallocate()|分别表示分配与释放内存空间。
\begin{lstlisting}
template <typename T>
class Allocator {
public:
    virtual T * allocate() = 0;
    virtual void deallocate(T * p) = 0;
};
\end{lstlisting}

\paragraph{搭建对象池类的框架}
这里模板参数\verb|Allocator|就是分配的策略接口。
\begin{lstlisting}
#include <stdexcept>    
    
template <typename T, typename Allocator>    
class ObjectPool {
public:
    ObjectPool() = default;
    ~ObjectPool() = default;
    
    void * allocate(size_t n) {
        if (sizeof(T) != n)
            throw std::bad_alloc();
        return m_allocator.allocate();
    }
    
    void deallocate(void * p) {
        m_allocator.deallocate(static_cast<T *>(p));  // 需要类型转换，将void*转换为T*
    }
    
private:
    Allocator m_allocator;
}
\end{lstlisting}

\paragraph{编写一个分配策略来测试对象池框架}
编写一个简单的分配策略\verb|MallocAllocator|
\begin{lstlisting}
template <typename T>
class MallocAllocator : public Allocator<T> {
public:
    MallocAllocator() = default;
    ~MallocAllocator() = default;
    
    virtual T * allocate() {
        auto p = ::malloc(sizeof(T));
        return reinterpret_cast<T*>(p);
    }
    
    virtual void deallocate(T * p) {
        ::free(p);
    }
}
\end{lstlisting}

编写测试代码，编写一个类\verb|A|，并用对象池来管理它，其中，分配策略使用的是\verb|MallocAllocator|
\begin{lstlisting}
class A {
private:
    typedef ObjectPool<A, MallocAllocator<A>> ObjectPool;  // 用别名来指代较长的类型名
    static ObjectPool pool;
public:
    A() {
        std::cout << "A construct" << std::endl;
    }
    
    ~A() {
        std::cout << "A destruct" << std::endl;
    }

    void * operator new(size_t n) {
        std::cout << "A new" << std::endl;
        return pool.allocate(n);
    }
    
    void oprator delete(void * p) {
        std::cout << "A delete" << std::endl;
        pool.deallocate(p);
    }
};

A::ObjectPool A::pool;  // 初始化static成员
\end{lstlisting}

\paragraph{基于数组的策略}
申请一块连续的内存空间，划分为许多个对象。

有正在使用和未使用两种标记。

请求对象时，则从未使用的对象中选取一个开始使用。

回收对象时，则将他的正在使用标记修改为未使用。
\begin{lstlisting}
#include <stdexcept>
    
template <typename T, int N>  // N表示数组的大小
class ArrayAllocator ： public Allocator<T> {
public:
    ArrayAllocator() {
        for (int i = 0; i < N; i++)
            m_used[i] = false;
    }
    ~ArrayAllocator() = default;
    
    // 遍历m_used数组，直至找到一个使用状态为false的单元
    // 将这个单元的使用情况置为true
    // 将m_data中，偏移量为sizeof(T) * i的内存块的地址，强制转换为T *，并返回这个结果
    virtual T * allocate() {
        for (int i = 0; i < N; i++) {
            if (!m_used[i]) {
                m_used[i] = true;
                return reinterpret_cast<T *>(&m_data[sizeof(T) * i]);
            }
        }
        // 如果没有找到，则抛出异常
        throw std::bad_alloc();
    }
    
    virtual void deallocate(T * p) {
        auto i = ((unsigned char *)p - m_data) / sizeof(T);
        m_used[i] = false;
    }
        
private:
    unsigned char m_data[sizeof(T) * N];
    bool m_used[N];


}
\end{lstlisting}

\paragraph{基于堆的策略}
数组的时间复杂度较高，如果频繁进行遍历比较浪费时间。

堆的分配策略，与数组的分配策略相比，也同样要申请一块连续的内存空间，并将他划分为多个相同大小的对象。

但是在查找和分配的时候，与数组的方法不同，数组采用的是遍历的方式。而基于堆的分配方式，会使用堆的思想，以更低的时间复杂度来在数组上查找和分配未使用的对象。
\begin{lstlisting}
#include <algorithm>    
    
template <typename T, int N>  // N表示数组的大小
class HeapAllocator ： public Allocator<T> {
public:
    enum State {
        FREE = 1;
        USED = 0;
    };
    struct Entry {
        State state;  // 状态
        T * p;  // 指针
        
        // 因为要建立堆，堆的个体之间必须要进行比较，所以重载一下小于号运算符
        bool operator<(const Entry & other) const {
            return state < other.state;
        }
    };
    
    HeapAllocator() {
        m_available = N;
        for (int i = 0; i < N; i++) {
            m_entry[i].state = FREE;
            m_entry[i].p = reinterpret_cast<T*>(&m_data[sizeof(T) * i]);
        }
    
        // 大根堆，根节点比子节点大
        // 因为FREE = 1，所以所有FREE的节点都会靠前拜访
        std::make_heap(m_entry, m_entry + N);
    }
    ~HeapAllocator() = default;
    
    virtual T * allocate() {
        if(m_available <= 0)  // 已满
            throw std::bad_alloc();
            
        Entry e = m_entry[0];  // 大根堆，将第一个元素保存到临时变量中
        std::pop_heap(m_entry, m_entry + N);  // 弹出第一个元素
        m_available--;
        m_entry[m_available].state = USED;
        m_entry[m_available].p = nullptr;
        
        return e.p;
    }
    
    virtual void deallocate(T * p) {
        if(p == nullptr || m_available >= N) {
            return;
        }
        
        m_entry[m_available].state = FREE;
        m_entry[m_available].p = reinterpret_cast<T*>(p);
        m_available++;
        std::push_heap(m_entry, m_entry + N);
    }

private:
    unsigned char m_data[sizeof(T) * N];
    Entry m_entry[N];
    int m_available;  // 表示当前未使用的对象的数量
}
\end{lstlisting}

\paragraph{基于栈的策略}
数组策略，时间复杂度是$O(N)$，而堆策略其时间复杂度为$O\log (N)$。栈策略，时间复杂度能控制在$O(1)$。

栈策略，仍然需要分配一块连续的内存空间，并把他划分给多个对象用于初始化，这一点类似于数组策略。

申请对象资源，首先从数组的一端开始，这里有一个用于表示位置的\verb|allocated|变量，指向下一个可用的对象。

对象使用完毕之后需要归还，不直接调整\verb|allocated|变量，而是通过一个栈结构来管理。将使用完毕的对象压入栈中(一般是对象的指针)。遇到下一次申请对象的情况，先检查栈中是否有对象，如果栈非空，则从栈中弹出一个对象去执行任务。

\chapter{高并发模型}
\section{Actor模型}
简单点说，actor通过消息传递的方式与外界通信。消息传递是异步的。每个actor都有一个邮箱，该邮箱接收并缓存其他actor发过来的消息，actor一次只能同步处理一个消息，处理消息过程中，除了可以接收消息，不能做任何其他操作。

每一个类独立在一个线程里称作Actor，Actor之间通过队列通信，比如Actor1 发消息给Actor2， Actor2 发消息给Actor1都是投递到对方的队列中。好像给对方发邮件，对方从邮箱中取出一样。

\subsubsection{开源实现caf}
基于C++的Actor模型，有CAF，是一个C++11风格的实现方案。caf的实现参考了akka和erlang

默许的actor调度是基于线程池方式, 而不是协程. 因此更像akka. 由于caf是基于c++的实现,在性能上可能更占优势.  更让人没法谢绝的是在caf中大量使用了lambda表达式来替换通常"散落一地"的回调函数.  视觉上非常直观, 开发时可以少死很多脑细胞.  固然这个项目现在还非常新, 网络io部份也比较瘦, 养肥了再用到生产环境中不迟

CAF中将actor分为两种，无类型actor和强类型actor。

\paragraph{项目地址}
项目地址是: \href{https://github.com/actor-framework/actor-framework}{https://github.com/actor-framework/actor-framework}

编译使用的是cmake, 进程很简单, 没甚么可说的.  编译完以后, 会生成两个库, \verb|libcaf_core.so| 和 \verb|libcaf_io.so.| 如果你不打算使用网络相干的功能, 只要链接上 \verb|libcaf_core| 就能够了.

\section{CSP模式}
CSP 是 Communicating Sequential Process 的简称，中文可以叫做通信顺序进程，是一种并发编程模型，是一个很强大的并发数据模型，是上个世纪七十年代提出的，用于描述两个独立的并发实体通过共享的通讯 channel(管道)进行通信的并发模型。相对于Actor模型，CSP中channel是第一类对象，它不关注发送消息的实体，而关注与发送消息时使用的channel。go是天然支持csp模式的语言。

CSP和Actor类似，只不过CSP将消息投递给channel，至于谁从channel中取数据，发送的一方是不关注的。简单的说Actor在发送消息前是直到接收方是谁，而接受方收到消息后也知道发送方是谁，更像是邮件的通信模式。而csp是完全解耦合的。

无论Actor还是CSP，他们都有一个共同的特性"Do not communicate by sharing memory; instead, share memory by communicating"，即，不使用共享内存来进行通信。

\section{Reactor模型}
\subsection{概述}
\paragraph{能否继续减轻线程池的线程切换成本}
让服务器服务多个客户端，那么多线程/多进程。线程/进程频繁创建、销毁，浪费时间。那么引入线程池，但是线程切换也有成本。

\paragraph{分析现有方案}
当采用多线程时(多进程与之类似)，线程的工作流程一般是"read，业务处理，send"。如果当前业务没有数据可读，则会阻塞在read那一步。如果此时采用的线程池，则浪费了一个线程资源。要解决这一个问题，最简单的方式就是将 socket 改成非阻塞，然后线程不断地轮询调用 read 操作来判断是否有数据。但是轮询的效率很低。

\paragraph{发现问题}
那有没有办法在只有当连接上有数据的时候，线程才去发起读请求呢？有的，这就是IO多路复用的3个技术路线(select, poll和epoll)，用一个线程去监控多个连接。

\paragraph{从IO多路复用引出Reactor模型}
IO多路复用的开发效率不高，因为还是面向过程编程。对它加上一层封装，就产生了reactor模式。

reactor中的react，指的是对事件反应。

Reactor 模式也叫 Dispatcher 模式，这个名字更贴合该模式的含义，即 I/O 多路复用监听事件，收到事件后，根据事件类型分配(Dispatch)给某个进程或线程。

\subsubsection{组成}
Reactor 模式主要由Reactor和处理资源池这两个核心部分组成。
\begin{enumerate}
    \item Reactor负责监听和分发事件，事件类型包含连接事件、读写事件。
    \item 处理资源池负责处理事件，如 read -> 业务逻辑 -> send。
\end{enumerate}

\subsection{分类}
Reactor的数量可以只有一个，也可以有多个。

处理资源池可以是单个进程/线程，也可以是多个进程或线程。

理论上就可以有 4 种方案选择
\begin{enumerate}
    \item 单 Reactor 单进程(线程)
    \item 单 Reactor 多进程(线程)
    \item 多 Reactor 单进程(线程)
    \item 多 Reactor 多进程(线程)
\end{enumerate}

其中，"多 Reactor 多进程(线程)"由于自身特性，复杂且性能低，所以在实际应用中基本不出现。

\paragraph{具体应用实例}
方案具体使用进程还是线程，要看使用的编程语言以及平台有关

Java 语言一般使用线程，比如 Netty

C 语言使用进程和线程都可以，例如 Nginx 使用的是进程，Memcache 使用的是线程

\subsection{单 Reactor 单进程(线程)}
一般来说，C 语言实现的是"单 Reactor 单进程"的方案，因为 C 语编写完的程序，运行后就是一个独立的进程，不需要在进程中再创建线程。而 Java 语言实现的是"单 Reactor 单线程"的方案，因为 Java 程序是跑在 Java 虚拟机这个进程上面的，虚拟机中有很多线程，我们写的 Java 程序只是其中的一个线程而已。

\paragraph{组成}
\begin{enumerate}
    \item Reactor 对象的作用是监听和分发事件；
    \item Acceptor 对象的作用是获取连接；
    \item Handler 对象的作用是处理业务；
\end{enumerate}

对象里的 select、accept、read、send 是系统调用函数，dispatch 和"业务处理"是需要完成的操作，其中 dispatch 是分发事件操作。
\begin{figure}[h]\centering
    \caption{单Reactor单进程(线程)}\label{单Reactor单进程(线程)}
    \includegraphics[width=0.8\textwidth]{单Reactor单进程(线程).png}
\end{figure}

\subsubsection{工作流程}
\begin{enumerate}
    \item Reactor 对象通过 select (IO 多路复用接口)监听事件，收到事件后通过 dispatch 进行分发，具体分发给 Acceptor 对象还是 Handler 对象，还要看收到的事件类型
    \item 如果是连接建立的事件，则交由 Acceptor 对象进行处理，Acceptor 对象会通过 accept 方法 获取连接，并创建一个 Handler 对象来处理后续的响应事件
    \item 如果不是连接建立事件， 则交由当前连接对应的 Handler 对象来进行响应
    \item Handler 对象通过 read -> 业务处理 -> send 的流程来完成完整的业务流程。
\end{enumerate}

\subsubsection{优缺点}
\paragraph{优势}
实现起来比较简单，不需要考虑进程间通信，也不用担心多进程竞争

\paragraph{缺陷}
因为只有一个进程，无法充分利用 多核 CPU 的性能；

Handler 对象在业务处理时，整个进程是无法处理其他连接的事件的，如果业务处理耗时比较长，那么就造成响应的延迟；

单 Reactor 单进程的方案不适用计算机密集型的场景，只适用于业务处理非常快速的场景

\paragraph{具体应用}
Redis 是由 C 语言实现的，它采用的正是"单 Reactor 单进程"的方案，因为 Redis 业务处理主要是在内存中完成，操作的速度是很快的，性能瓶颈不在 CPU 上，所以 Redis 对于命令的处理是单进程的方案。

\subsection{单 Reactor 多进程(线程)}
\subsubsection{工作流程}
\begin{enumerate}
    \item Reactor 对象通过 select (IO 多路复用接口) 监听事件，收到事件后通过 dispatch 进行分发，具体分发给 Acceptor 对象还是 Handler 对象，还要看收到的事件类型
    \item 如果是连接建立的事件，则交由 Acceptor 对象进行处理，Acceptor 对象会通过 accept 方法 获取连接，并创建一个 Handler 对象来处理后续的响应事件；
    \item 如果不是连接建立事件， 则交由当前连接对应的 Handler 对象来进行响应，上面的三个步骤和单 Reactor 单线程方案是一样的，接下来的步骤就开始不一样了
    \item Handler 对象不再负责业务处理，只负责数据的接收和发送，Handler 对象通过 read 读取到数据后，会将数据发给子线程里的 Processor 对象进行业务处理；
    \item 子线程里的 Processor 对象就进行业务处理，处理完后，将结果发给主线程中的 Handler 对象，接着由 Handler 通过 send 方法将响应结果发送给 client；
\end{enumerate}

\begin{figure}[h]\centering
    \caption{单Reactor多进程(线程)}\label{单Reactor多进程(线程)}
    \includegraphics[width=0.8\textwidth]{单Reactor多进程(线程).png}
\end{figure}

\subsubsection{优缺点}
\paragraph{优势}
能够充分利用多核CPU。

\paragraph{缺陷}
多线程、多进程随之带来了\textbf{数据竞争}的问题。

多进程比起多线程更复杂，涉及到进程间通信的问题。(而多线程可以巧妙利用共享内存)

因为一个 Reactor 对象承担所有事件的监听和响应，而且只在主线程中运行，在面对瞬间高并发的场景时，容易成为\textbf{性能的瓶颈}的地方。

\subsection{多 Reactor 多进程(线程)}
\subsubsection{工作流程}
\begin{enumerate}
    \item 主线程中的 MainReactor 对象通过 select 监控连接建立事件，收到事件后通过 Acceptor 对象中的 accept 获取连接，将新的连接分配给某个子线程
    \item 子线程中的 SubReactor 对象将 MainReactor 对象分配的连接加入 select 继续进行监听，并创建一个 Handler 用于处理连接的响应事件
    \item 如果有新的事件发生时，SubReactor 对象会调用当前连接对应的 Handler 对象来进行响应
    \item Handler 对象通过 read -> 业务处理 -> send 的流程来完成完整的业务流程
\end{enumerate}

\begin{figure}[h]\centering
    \caption{多Reactor多进程(线程)}\label{多Reactor多进程(线程)}
    \includegraphics[width=0.7\textwidth]{多Reactor多进程(线程).png}
\end{figure}

\subsubsection{优缺点}
\paragraph{优势}
多 Reactor 多线程的方案虽然看起来复杂的，但是实际实现时比单 Reactor 多线程的方案要简单的多。
\begin{enumerate}
    \item 主线程和子线程分工明确，主线程只负责接收新连接，子线程负责完成后续的业务处理。
    \item 主线程和子线程的交互很简单，主线程只需要把新连接传给子线程，子线程无须返回数据，直接就可以在子线程将处理结果发送给客户端。
\end{enumerate}

\paragraph{具体应用}
大名鼎鼎的两个开源软件 Netty 和 Memcache 都采用了"多 Reactor 多线程"的方案。

采用了"多 Reactor 多进程"方案的开源软件是 Nginx，不过方案与标准的多 Reactor 多进程有些差异。

具体差异表现在主进程中仅仅用来初始化 socket，并没有创建 mainReactor 来 accept 连接，而是由子进程的 Reactor 来 accept 连接，通过锁来控制一次只有一个子进程进行 accept (防止出现惊群现象)，子进程 accept 新连接后就放到自己的 Reactor 进行处理，不会再分配给其他子进程。

\section{Proactor模型}
\subsection{概述}
\subsubsection{与Reactor的区别}
前面提到的 Reactor 是非阻塞同步网络模式，而 Proactor 是异步网络模式。

Reactor 是非阻塞同步网络模式，感知的是就绪可读写事件。在每次感知到有事件发生(比如可读就绪事件)后，就需要应用进程主动调用 read 方法来完成数据的读取，也就是要应用进程主动将 socket 接收缓存中的数据读到应用进程内存中，这个过程是同步的，读取完数据后应用进程才能处理数据。

Proactor 是异步网络模式， 感知的是已完成的读写事件。在发起异步读写请求时，需要传入数据缓冲区的地址(用来存放结果数据)等信息，这样系统内核才可以自动帮我们把数据的读写工作完成，这里的读写工作全程由操作系统来做，并不需要像 Reactor 那样还需要应用进程主动发起 read/write 来读写数据，操作系统完成读写工作后，就会通知应用进程直接处理数据。

Reactor 可以理解为“来了事件操作系统通知应用进程，让应用进程来处理”，而 Proactor 可以理解为“来了事件操作系统来处理，处理完再通知应用进程”

无论是 Reactor，还是 Proactor，都是一种基于“事件分发”的网络编程模式，区别在于 Reactor 模式是基于“待完成”的 I/O 事件，而 Proactor 模式则是基于“已完成”的 I/O 事件。

\subsubsection{阻塞与非阻塞}
指应用程序在发起 I/O 操作时，是立即返回还是等待

\paragraph{同步阻塞IO}
用户线程发起 read 调用后就阻塞了，让出 CPU。内核等待网卡数据到来，把数据从网卡拷贝到内核空间，接着把数据拷贝到用户空间，再把用户线程叫醒

阻塞 I/O，当用户程序执行 read，线程会被阻塞，一直等到内核数据准备好，并把数据从内核缓冲区拷贝到应用程序的缓冲区中，当拷贝过程完成，read 才会返回。

注意，阻塞等待的是“内核数据准备好”和“数据从内核态拷贝到用户态”这两个过程。

\paragraph{同步非阻塞IO}
用户线程不断的发起 read 调用，数据没到内核空间时，每次都返回失败，直到数据到了内核空间，这一次 read 调用后，在等待数据从内核空间拷贝到用户空间这段时间里，线程还是阻塞的，等数据到了用户空间再把线程叫醒

非阻塞的 read 请求在数据未准备好的情况下立即返回，可以继续往下执行，此时应用程序不断轮询内核，直到数据准备好，内核将数据拷贝到应用程序缓冲区，read 调用才可以获取到结果。

最后一次 read 调用，获取数据的过程，是一个同步的过程，是需要等待的过程。这里的同步指的是内核态的数据拷贝到用户程序的缓存区这个过程。

\paragraph{异步IO}
从上文可知，无论是阻塞IO还是非阻塞IO都是需要同步的，在最后一个read动作执行之前都需要同步。

真正的异步 I/O 是“内核数据准备好”和“数据从内核态拷贝到用户态”这两个过程都不用等待。

当我们发起异步I/O之后，就立即返回，内核自动将数据从内核空间拷贝到用户空间，这个拷贝过程同样是异步的，内核自动完成的，和前面的同步操作不一样，应用程序并不需要主动发起拷贝动作。

\paragraph{区别}
阻塞 I/O 好比，你去饭堂吃饭，但是饭堂的菜还没做好，然后你就一直在那里等啊等，等了好长一段时间终于等到饭堂阿姨把菜端了出来(数据准备的过程)，但是你还得继续等阿姨把菜(内核空间)打到你的饭盒里(用户空间)，经历完这两个过程，你才可以离开。

非阻塞 I/O 好比，你去了饭堂，问阿姨菜做好了没有，阿姨告诉你没，你就离开了，过几十分钟，你又来饭堂问阿姨，阿姨说做好了，于是阿姨帮你把菜打到你的饭盒里，这个过程你是得等待的。

异步 I/O 好比，你让饭堂阿姨将菜做好并把菜打到饭盒里后，把饭盒送到你面前，整个过程你都不需要任何等待。

很明显，异步 I/O 比同步 I/O 性能更好，因为异步 I/O 在"内核数据准备好"和"数据从内核空间拷贝到用户空间"这两个过程都不用等待。

\subsection{工作流程}
\begin{enumerate}
    \item Proactor Initiator 负责创建 Proactor 和 Handler 对象，并将 Proactor 和 Handler 都通过Asynchronous Operation Processor 注册到内核；
    \item Asynchronous Operation Processor 负责处理注册请求，并处理 I/O 操作；
    \item Asynchronous Operation Processor 完成 I/O 操作后通知 Proactor；
    \item Proactor 根据不同的事件类型回调不同的 Handler 进行业务处理；
    \item Handler 完成业务处理
\end{enumerate}

\begin{figure}[!ht]
    \centering
    \label{proactor工作流程}\caption{proactor工作流程}
    \includegraphics[width=0.8\textwidth]{proactor工作流程.jpg}
\end{figure}

\subsection{具体应用}
可惜的是，在 Linux 下的异步 I/O 是不完善的，aio 系列函数是由 POSIX 定义的异步操作接口，不是真正的操作系统级别支持的，而是在用户空间模拟出来的异步，并且仅仅支持基于本地文件的 aio 异步操作，网络编程中的 socket 是不支持的，这也使得基于 Linux 的高性能网络程序都是使用 Reactor 方案。

而 Windows 里实现了一套完整的支持 socket 的异步编程接口，这套接口就是 IOCP，是由操作系统级别实现的异步 I/O，真正意义上异步 I/O，因此在 Windows 里实现高性能网络程序可以使用效率更高的 Proactor 方案。

\chapter{网络编程}
\section{基础库函数}
\subsection{字节序}
\subsubsection{字节序}
通信双方交流的信息单元(比特、字节、字、双字等)应该以什么样的顺序进行传送。

存储数据的顺序，即字节序问题，一般说的是，大于一个字节的数据在内存中的存放数据。比如一个数据\verb|0x1234|，他是2个字节组成，那么他在内存中存放的方式是\verb|0x12|和\verb|0x34|，还是\verb|0x34|和\verb|0x12|，这是两种存放的方式。

1个字节是8个bit，1个字节可以用2位十六进制数表示，可以表示\verb|0x00~0xFF|的范围。

单字符，和字符串，都不需要面临字节序的问题。他们的最小分配单位，都刚好是一个字节。但是\verb|int|和\verb|long|类型，他的长度都不止1个字节，所以需要考虑字节序问题。

\paragraph{两种字节序}
\begin{itemize}
    \item 大端模式 (big endian)，又称为网络字节序。
    \item 小端模式 (small endian)，又称为主机字节序。
\end{itemize}

x86是小端，arm是大端。

\paragraph{判断字节序的方法}
判断字节序的方法有很多。这里只给出示例。

用一个\verb|union|类型，里面存放一个\verb|int|或者一个\verb|char[4]|，通过\verb|char|类型，将\verb|int|打印出来，看看内存中的排列顺序。
\begin{lstlisting}
#include <iostream>

int main() {
    int num = 1;
    // 将int类型指针转换为char类型指针，取第一个字节
    char* ptr = reinterpret_cast<char*>(&num);
    
    if (*ptr == 1) {
        std::cout << "Little-endian" << std::endl;
    } else {
        std::cout << "Big-endian" << std::endl;
    }
    return 0;
}    
\end{lstlisting}

\subsubsection{ip地址转换}
头文件\verb|arpa/inet.h|

\verb|inet_ntop()|，将IPv4或IPv6 Internet网络地址转换为 Internet标准格式的字符串。

\verb|inet_pton()|，主机字节序的IP地址转换为网络字节序，主机字节序的IP地址是字符串，网络字节序IP地址是整型。

函数中p和n分别代表表达(presentation)和数值(numeric)。

\verb|inet_addr()|，点分十进制IP转换为大端整型(只适用于IPv4)

\verb|inet_ntoa()|，大端整型转换为点分十进制(只适用于IPv4)

\section{socket通信}
\subsection{工作流程}
\paragraph{服务端}
\begin{enumerate}
    \item 创建用于监听的套接字，这个套接字是一个文件描述符。来自UNIX的"一切皆文件"思想。socket()
    \item 将文件描述符与本地IP端口进行绑定。bind()
    \item 设置监听，成功之后开始监听。listen()
    \item 等待并接受客户端的连接请求，建立新的链接，会得到一个新的文件描述符。accept()
    \item 进行通信过程，读或者写，读写操作默认的都是阻塞的。read(), recv(), write(), send()
    \item 断开连接，关闭套接字。close()
\end{enumerate}

\paragraph{客户端}
\begin{enumerate}
    \item 创建用于通信的套接字。socket()
    \item 初始化，连接服务器，需要知道服务器绑定的IP和端口。connect()
    \item 通信。read(), recv(), write(), send()
    \item 断开连接。close()
\end{enumerate}

服务端创建监听套接字，包含3步，socket，bind和listen

在完成上述三步之后，服务器端就可以接收客户端的连接请求了。

为了能及时地收到客户端的连接请求，一般情况下会通过 while 循环处理，在循环中调用 accept 函数，用于接收客户端连接请求。

这里需要注意的是，accept 函数是阻塞函数，也就是说，如果此时一直没有客户端连接请求，那么，服务器端的执行流程会一直阻塞在 accept 函数。一旦有客户端连接请求到达，accept 将不再阻塞，而是处理连接请求，和客户端建立连接，并返回已连接套接字(Connected Socket)。

\subsubsection{使用原生socket通信的缺陷}
\paragraph{自身的阻塞性无法处理多个请求}
程序每调用一次 accept 函数，只能处理一个客户端连接。因此，如果想要处理多个并发客户端的请求，我们就需要使用多线程的方法，来处理通过 accept 函数建立的多个客户端连接上的请求。

使用这种方法后，我们需要在 accept 函数返回已连接套接字后，创建一个线程，并将已连接套接字传递给创建的线程，由该线程负责这个连接套接字上后续的数据读写。同时，服务器端的执行流程会再次调用 accept 函数，等待下一个客户端连接。

这就是通过\textbf{多线程}来实现服务器并发的技术路线

\paragraph{创建、切换线程也带来大量开销}
缺陷在于，非常消耗线程资源，线程上下文切换的开销也非常大

改进方向是，不要创建那么多的线程，也就同时消除了线程之间切换的开销。

所以引出了\textbf{IO多路复用}的技术路线。

\section{IO多路复用}
\paragraph{为什么引出IO多路复用}
server端实现并发的3种方式，多进程，多线程，和IO多路复用。

如果server端由于某些原因，只能使用单进程或单线程，那么就可以使用IO多路复用。IO多路复用，只需要单进程/单线程就能够实现server端并发。

\paragraph{原理}
两类文件描述符，负责监听的和负责通信的。

每个文件描述符，都有2个缓冲区，分别是读和写缓冲区。监听的accept()，读数据的read()，写数据的write()，都是互斥的，也就是说如果有一个函数阻塞住了，其他函数也不会运行，都会等待。这就是单进程/单线程如果不做任何更改时的局限性，它是不能实现并发的。

IO多路复用，是操作系统内核对所有的文件描述符，进行统一的遍历，如果其为空，并不会阻塞，而是再看其他的文件描述符，如果为非空，那么久按需进行accept()/read()/write()操作。这样就解开了阻塞。

\paragraph{需要注意的点}
\begin{enumerate}
    \item 多路复用机制会监听套接字上的哪些事件？
    \item 多路复用机制可以监听多少个套接字？
    \item 当有套接字就绪时，多路复用机制要如何找到就绪的套接字？
\end{enumerate}

\paragraph{分类}
IO多路复用，有3种方式，select, poll, epoll。
\begin{enumerate}
    \item select可以跨平台，windows/linux/mac都可以使用，而poll和epoll都只能在linux中使用。
    \item epoll的底层实现是红黑树，select和poll的底层实现是线性表。
    \item select可检测的client端的个数是有上限的(1024个)，poll/epoll是没有上限的。
\end{enumerate}

着重掌握select和epoll。

\subsection{select}
\paragraph{select()函数}
\begin{lstlisting}
int select (int __nfds, fd_set *__readfds, fd_set *__writefds, fd_set *__exceptfds, struct timeval *__timeout)
\end{lstlisting}

\paragraph{监听的文件描述符数量}
监听的文件描述符数量 \verb|__nfds|，三个文件描述符集合个数最大值+1。在windows里，这个参数指定为-1即可。

\paragraph{被监听描述符}
被监听描述符的三个集合 \verb|__readfds|、\verb|__writefds| 和 \verb|__exceptfds|，读、写、异常文件描述符的指针，指向文件描述符的集合。

都是传入传出参数，也就是\verb|select()|函数调用结束之后，结果也会体现在\verb|__readfds|、\verb|__writefds| 和 \verb|__exceptfds|这三个参数中，也即他们所指向的内存空间。

假如\verb|__readfds|参数传入了50个文件描述符，\verb|select()|函数内部对这50个文件描述符进行检测了之后发现，只有5个有内容需要读，剩下45都是空的，没有内容需要读，则\verb|select()|函数就把\verb|__readfds|指向的集合中这45个空的文件描述符都删除掉，留下5个。这就是"传入传出参数"的含义，内核运行\verb|select()|函数会修改这一块内容，先读再改，双向的。

\paragraph{超时时长}
监听时阻塞等待的超时时长\verb|__timeout|。

\paragraph{跨平台性}
\verb|select()|函数是跨平台的。

\paragraph{阻塞性}
程序调用\verb|select()|函数会阻塞，当\verb|select()|函数发现有文件描述符处于就绪状态后，才会结束阻塞。

并不保证只要有1个文件描述符就绪就会立刻结束阻塞，可能有多个文件描述符就绪，以集合的形式返回。

\paragraph{文件描述符表}
server端，能够监听的文件描述符的个数是n+1，n是负责通信的，1是负责监听的，负责监听的文件描述符只有1个。

启动一个进程，就会得到一个对应的虚拟地址空间，分为内核区和用户区两大部分。在内核区有专门用于进程管理的模块，linux中叫进程控制块PCB(process control block)，本质是一个叫做\verb|task_struct|的结构体，里边包含进程管理的各种信息。其中有一个结构体叫做\verb|file|，就是文件描述符表，里面有一个整型索引表，用于存储文件描述符。

内核为每一个进程维护了一个文件描述符表，索引表中的值都是从0开始的。不同的进程中，可能看到相同的文件描述符，但是他们指向的是不同的文件。

\verb|fdset|类型，1024 bits，128字节，32个整型数。外观上看上去是一个\verb|int[32]|数组，但是具体使用的时候，是要把他展开成一个二进制数。由于文件描述符表的索引，是从0开始逐个增加的整型数，那么，index为200的文件描述符，就对应到\verb|fdset|中index为200的bit，如果他可写/读，则置为1，否，则置为0。

操作函数，依据文件描述符表对\verb|fdset|进行增删改查，\verb|FD_CLR()|, \verb|FD_ISSET()|, \verb|FD_SET()|, \verb|FD_ZERO()|

\paragraph{工作流程}
\begin{enumerate}
    \item \verb|socket()|创建套接字
    \item \verb|bind()|绑定套接字
    \item \verb|listen()|开始监听
    \item \verb|FD_ZERO()|，初始化一个读的文件描述符的集合，因为负责监听的文件描述符需要一直读新的信息，新的信息指的就是client发来的连接请求
    \item \verb|FD_SET()|，把负责监听的文件描述符，初始化为1
    \item \verb|select()|，循环，超时处理
    \item 如果\verb|select()|返回值是1，那么就要调用\verb|FD_ISSET()|，看看这个文件描述符属于哪一类，看看需要调用\verb|accept()|，\verb|recv()|或者其他函数。
\end{enumerate}

首先，通过 select 查询哪些套接字处于就绪状态。

首先，我们在调用 select 函数前，可以先创建好传递给 select 函数的描述符集合，然后再创建监听套接字。

而为了让创建的监听套接字能被 select 函数监控，我们需要把这个套接字的描述符加入到创建好的描述符集合中。

然后，我们就可以调用 select 函数，并把创建好的描述符集合作为参数传递给 select 函数。

程序在调用 select 函数后，会发生阻塞。

而当 select 函数检测到有描述符就绪后，就会结束阻塞，并返回就绪的文件描述符个数。那么此时，我们就可以在描述符集合中查找哪些描述符就绪了。然后，我们对已就绪描述符对应的套接字进行处理。

然后，通过系统调用读、写数据

比如，如果是 \verb|__readfds| 集合中有描述符就绪，这就表明这些就绪描述符对应的套接字上，有读事件发生，此时，我们就在该套接字上读取数据。

而因为 select 函数一次可以监听 1024 个文件描述符的状态，所以 select 函数在返回时，也可能会一次返回多个就绪的文件描述符。这样一来，我们就可以使用一个循环流程，依次对就绪描述符对应的套接字进行读写或异常处理操作。

\paragraph{缺陷与改进}
select 函数存在两个设计上的不足
\begin{enumerate}
    \item 首先，\verb|select()| 函数对单个进程能监听的文件描述符数量是有限制的，它能监听的文件描述符个数由 \verb|__FD_SETSIZE| 决定，默认值是 1024。
    \item 其次，当 \verb|select()| 函数返回后，我们需要遍历描述符集合，才能找到具体是哪些描述符就绪了。这个遍历过程会产生一定开销，从而降低程序的性能。
\end{enumerate}

所以，为了解决 \verb|select()| 函数受限于 1024 个文件描述符的不足，\verb|poll()| 函数对此做了改进。

\subsection{poll}
\paragraph{poll()函数}


\begin{lstlisting}
int poll (struct pollfd *__fds, nfds_t __nfds, int __timeout);
\end{lstlisting}

\paragraph{参数}
\begin{enumerate}
    \item \verb|__fds| 是 \verb|pollfd| 结构体数组。
    \item \verb|__nfds|表示\verb|__fds|数组的元素个数
    \item \verb|__timeout| 表示 \verb|poll()| 函数阻塞的超时时间。
\end{enumerate}

\paragraph{pollfd结构体}
\verb|pollfd| 结构体中包含了三个成员变量 \verb|fd|、\verb|events| 和 \verb|revents|，分别表示要监听的文件描述符、要监听的事件类型和实际发生的事件类型。

\verb|revent| 是一个传出参数，\verb|r| 代表 \verb|return|。
\begin{lstlisting}
struct pollfd {
    int fd;  //进行监听的文件描述符
    short int events;  //要监听的事件类型
    short int revents;  //实际发生的事件类型
};
\end{lstlisting}

其中，要监听和实际发生的事件类型，是通过以下三个宏定义来表示的，分别是 \verb|POLLRDNORM|、\verb|POLLWRNORM| 和 \verb|POLLERR|，它们分别表示可读、可写和错误事件。
\begin{lstlisting}
#define POLLRDNORM  0x040  //可读事件
#define POLLWRNORM  0x100  //可写事件
#define POLLERR     0x008  //错误事件
\end{lstlisting}

\paragraph{工作流程}
\begin{enumerate}
    \item 创建 \verb|pollfd| 数组和监听套接字，并进行绑定
    \item 将监听套接字加入 \verb|pollfd| 数组，并设置其监听读事件，也就是客户端的连接请求
    \item 循环调用 \verb|poll()| 函数，检测 \verb|pollfd| 数组中是否有就绪的文件描述符
    \begin{itemize}
        \item 如果是连接套接字就绪，这表明是有客户端连接，我们可以调用 accept 接受连接，并创建已连接套接字，并将其加入 pollfd 数组，并监听读事件；
        \item 如果是已连接套接字就绪，这表明客户端有读写请求，我们可以调用 recv/send 函数处理读写请求。
    \end{itemize}
\end{enumerate}

总的来说，流程上和 select 类似，先查询哪些套接字处于就绪状态(使用poll方法查询)，然后通过系统调用进行事件的读写。

\paragraph{缺陷与改进}
和 select 函数相比，poll 函数的改进之处主要就在于，它允许一次监听超过 1024 个文件描述符。但是当调用了 poll 函数后，我们仍然需要遍历每个文件描述符，检测该描述符是否就绪，然后再进行处理。

那么，有没有办法可以避免遍历每个描述符呢？ 这就是接下来介绍的 epoll 机制。

\subsection{epoll}
\paragraph{概述}
epoll 在 Linux2.6 内核正式提出，是基于事件驱动的 I/O 方式，相对于 select 来说，epoll 没有描述符个数限制，使用一个文件描述符管理多个描述符，将用户关心的文件描述符的事件存放到内核的一个事件表中，这样在用户空间和内核空间的 COPY 只需一次。

底层数据结构是红黑树

回调，基于事件来处理，发生了什么就相应地有什么行为，没有发生任何事件就不要有任何行为。

select和poll工作过程中，存在内核空间和用户空间频繁的数据交换问题，而在epoll中，内核空间和用户空间使用的是共享内存SHM(基于mmap内存映射区实现)，省去了不必要的内存拷贝。

\paragraph{相关函数}
\verb|epoll_create|，创建epoll实例，通过红黑树来管理待检测集合。
\begin{lstlisting}
int epoll_create(int size);
\end{lstlisting}

\verb|epoll_ctl|，操作红黑树，对节点进行增删改
\begin{lstlisting}
int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);
\end{lstlisting}

\begin{enumerate}
    \item \verb|epfd| 表示 \verb|epoll| 句柄
    \item \verb|op| 表示 \verb|fd| 操作类型，有如下3种
    \begin{itemize}
        \item \verb|EPOLL_CTL_ADD| 注册新的 \verb|fd| 到 \verb|epfd| 中
        \item \verb|EPOLL_CTL_MOD| 修改已注册的 \verb|fd| 的监听事件
        \item \verb|EPOLL_CTL_DEL| 从 \verb|epfd| 中删除一个 \verb|fd|
    \end{itemize}
    \item \verb|fd| 是要监听的描述符
    \item \verb|event| 表示要监听的事件
\end{enumerate}

\verb|epoll_wait|，检测红黑树上是否有就绪的文件操作符，如果有就绪的节点就返回节点的个数，调用失败时返回 -1，等待超时返回 0。
\begin{lstlisting}
int epoll_wait(int epfd, struct epoll_event * events, int maxevents, int timeout);
\end{lstlisting}

节点的信息存储在第2个参数中\verb|struct epoll_event * events|，后续可以根据这个信息对节点进行操作。如果epoll树上暂时没有就绪的文件操作符，则这个函数会阻塞在这里。
\begin{enumerate}
    \item \verb|epfd| 是 \verb|epoll| 句柄
    \item \verb|events| 表示从内核得到的就绪事件集合
    \item \verb|maxevents| 告诉内核 \verb|events| 的大小
    \item \verb|timeout| 表示等待的超时事件
\end{enumerate}

\paragraph{工作流程}
\begin{enumerate}
    \item \verb|socket()|, \verb|bind()|, \verb|listen()|，其中还有一步，是设置端口复用，调用\verb|setsockopt()|函数，这个是可选的，不是必须的。
    \item 创建\verb|epoll|实例对象\verb|int epfd = epoll_create(100);|。\verb|epoll_create| 函数创建一个 \verb|epoll| 句柄，由内核进行维护。
    \item 将用于监听的文件描述符，添加到\verb|epoll|实例中。\verb|int ret = epoll_ctl(epfd, EPOLL_CTL_ADD, lfd, &s_epoll_event);|。\verb|epoll_ctl| 函数注册要监听的套接字、事件类型。
    \item 调用\verb|epoll_wait()|进行检测，如果有就绪的文件描述符，则函数会返回一个个数，如果没有的话函数会阻塞。\verb|epoll_wait| 函数等待事件的就绪，成功时返回就绪的事件数目，并且通过引用返回就绪状态的 \verb|fd|。
\end{enumerate}

epoll 实例内部维护了两个结构，"要监听的文件描述符"和"已经就绪的文件描述符"。而对于已经就绪的文件描述符来说，它们会被返回给用户程序进行处理(引用返回)。

值得说明的是，这里"已就绪的文件描述符"是由内核主动添加至就绪文件描述符集合中，我们从用户态调用 \verb|epoll_wait| 就直接查询该集合是否有就绪 I/O 事件，这样下来，就减少了全遍历所有文件描述符的操作。

所以，我们在使用 epoll 机制时，就不用像使用 select 和 poll 一样，遍历查询哪些文件描述符已经就绪了。这样一来， epoll 的效率就比 select 和 poll 有了更高的提升。

\paragraph{两种工作模式}
\begin{enumerate}
    \item 水平模式LT(level triggered)，是默认的工作模式，该模式支持阻塞和非阻塞套接字，如果想采用ET模式，可以使用EPOLLET参数
    \item 边沿模式ET(edge triggered)，也有的叫边缘触发，高速的工作模式
\end{enumerate}

当一个新的事件到来时，LT、ET模式下都可以通过 \verb|epoll_wait| 方法来获取到这个事件，但是如果这次没有把这个事件对应的套接字缓冲区处理完，在这个套接字没有新的事件再次到来时：

在ET模式下是无法再次从 \verb|epoll_wait| 方法中获取到这个事件的；

而LT模式则可以再次获取到，进而再次进行处理，其只要一个事件对应的套接字缓冲区还有数据，就总能从 \verb|epoll_wait| 中获取到这个事件，因此，如果采用ET模式进行开发时，要特别注意，如果没有彻底的将缓冲区中的数据处理完，则会导致缓冲区中的用户请求得不到响应。

总而言之就是ET模式下如果将该事件从\verb|rdlist|取出了，那么将不会第二次获取了。但是LT模式下当还有新的事件来到时，还会将其放入\verb|rdlist|。

\part{设计模式}
\chapter{设计模式六大原则}
\paragraph{开闭原则(Open Close Principle)}
\textbf{对扩展开放，对修改关闭}

不能修改原有代码，而是要扩展原有代码

\paragraph{单一职责原则}
一个类应该只有一个发生变化的原因

不要存在多于一个导致类变更的原因，也就是说每个类应该实现单一的职责，否则就应该把类拆分

\paragraph{里氏替换原则(Liskov Substitution Principle)}
所有引用基类的地方必须能透明地使用其子类的对象

里氏替换原则是继承复用的基石，只有当衍生类可以替换基类，软件单位的功能不受到影响时，基类才能真正被复用，而衍生类也能够在基类的基础上增加新的行为。

里氏代换原则是对开闭原则的补充

里氏替换原则中，子类对父类的方法尽量不要重写和重载。因为父类代表了定义好的结构，通过这个规范的接口与外界交互，子类不应该随便破坏它。

\paragraph{依赖倒置原则(Dependence Inversion Principle)}
这个原则是开闭原则的基础

上层模块不应该依赖底层模块，它们都应该依赖于抽象。

抽象不应该依赖于细节，细节应该依赖于抽象。

面向接口编程，依赖于抽象而不依赖于具体。写代码时用到具体类时，不与具体类交互，而与具体类的上层接口交互

\paragraph{接口隔离原则(Interface Segregation Principle)}
这个原则的意思是：使用多个隔离的接口，比使用单个接口要好。

它还有另外一个意思是：降低类之间的耦合度。由此可见，其实设计模式就是从大型软件架构出发、便于升级和维护的软件设计思想，它强调降低依赖，降低耦合。

客户端不应该依赖它不需要的接口。

类间的依赖关系应该建立在最小的接口上。每个接口中不存在子类用不到却必须实现的方法，如果不然，就要将接口拆分。使用多个隔离的接口，比使用单个接口(多个接口方法集合到一个的接口)要好

\paragraph{迪米特法则(最少知道原则)(Law of Demeter)}
只与你的直接朋友交谈，不跟陌生人说话。一个类对自己依赖的类知道的越少越好。无论被依赖的类多么复杂，都应该将逻辑封装在方法的内部，通过public方法提供给外部。这样当被依赖的类变化时，才能最小的影响该类。

最少知道原则的另一个表达方式是：只与直接的朋友通信。类之间只要有耦合关系，就叫朋友关系。耦合分为依赖、关联、聚合、组合等。我们称出现为成员变量、方法参数、方法返回值中的类为直接朋友。局部变量、临时变量则不是直接的朋友。我们要求陌生的类不要作为局部变量出现在类中

\paragraph{合成复用原则(Composite Reuse Principle)}
尽量使用对象组合/聚合，而不是继承关系达到软件复用的目的。合成或聚合可以将已有对象纳入到新对象中，使之成为新对象的一部分，因此新对象可以调用已有对象的功能

\chapter{创建型模式(5种)}

对类的示例化过程进行了抽象，将对象的创建和使用进行分离

\begin{enumerate}
    \item 工厂模式(Factory Pattern)
    \item 抽象工厂模式(Abstract Factory Pattern)
    \item 单例模式(Singleton Pattern)
    \item 建造者模式(Builder Pattern)
    \item 原型模式(Prototype Pattern)
\end{enumerate}

\section{工厂模式(Factory Pattern)}

用户给工厂一个需求，工厂创建一个产品，用户使用这个产品

用户和工厂之间通过共同的接口来指向新创建的对象

\subsection{缺陷}

\begin{itemize}
    \item 由于任何东西都可以被生产，所以工厂的代码量可能会变得很庞大
    \item 可能不满足开闭原则，因为增加新产品只能通过修改工厂方法来实现
\end{itemize}

\subsection{改进}

让某一类产品交给某一个工厂来负责，而不是像"工厂模式"那样一个工厂负责所有产品的生产

所以一旦需要扩展，则只需要增加一个生产这个新产品的新工厂即可

\section{抽象工厂模式(Abstract Factory Pattern)}

围绕一个超级工厂创建其他工厂

该超级工厂又称为其他工厂的工厂

\begin{itemize}
    \item \textbf{产品族}：一个品牌下面的所有产品；例如华为下面的手机，路由器，电脑称为华为的产品族
    \item \textbf{产品等级}：多个品牌下面的同种产品；例如华为和小米下面的手机称为一个产品等级
\end{itemize}

拓展一个产品族，例如产品族中新增一个笔记本电脑，也就是说华为和小米现在可以生产电脑了。需要修改旧代码

扩展一个产品等级，例如新增一个手机，也就是说新增一个品牌来生产手机。可以直接增加新代码，符合开闭原则。

\subsection{优点}

一个产品族中的多个对象被设计成一起工作时，它能保证客户端始终只使用同一个产品族中的对象(将一个系列的产品统一一起创建)

\subsection{缺点}

\begin{itemize}
    \item 产品族扩展非常困难，要增加一个系列的某一产品，既要修改工厂抽象类里加代码，又修改具体的实现类里面加代码
    \item 增加了系统的抽象性和理解难度
\end{itemize}

\subsection{适用场景}

\begin{itemize}
    \item 一系列相关产品对象(属于同一产品族)一起创建时需要大量的重复代码
    \item 提供一个产品类的库，所有的产品以同样的接口出现，从而使得客户端不依赖于具体的实现
\end{itemize}

\section{单例模式(Singleton Pattern)}
\subsection{含义}
类负责自己类型的对象的创建，而且只有一个

提供一个可以访问它的全局访问点

\subsection{解决的问题}
一个全局使用的类对象，避免频繁地创建与销毁。

\subsection{适用场景}
\begin{itemize}
    \item 一个班级只有一个班主任
    \item Windows是多进程多线程的，在操作一个文件的时候，就不可避免地出现多个进程或线程同时操作一个文件的现象，所以所有文件的处理必须通过唯一的实例来进行
    \item 一些设备管理器常常设计为单例模式，比如一个电脑有两台打印机，在输出的时候就要处理不能两台打印机打印同一个文件
    \item WEB中的计数器，不用每次刷新都在数据库里加一次，用单例先缓存起来
    \item 创建的一个对象需要消耗的资源过多，比如I/O与数据库的连接等
\end{itemize}

\subsection{优点}
\begin{itemize}
    \item 在内存里只有一个实例，减少了内存的开销，尤其是频繁的创建和销毁实例(比如管理学院首页页面缓存)
    \item 避免对资源的多重占用(比如写文件操作)
\end{itemize}

\subsection{缺点}
没有接口，不能继承，与单一职责原则冲突，一个类应该只关心内部逻辑，而不关心外面怎么样来实例化

\subsection{利用局部静态变量实现}
当一个函数中定义一个局部静态变量，那么这个局部静态变量只会初始化一次，就是在这个函数第一次调用的时候，以后无论调用几次这个函数，函数内的局部静态变量都不再初始化。

那我们可以利用局部静态变量这一特点实现单例

\begin{lstlisting}
// 博主: 恋恋风辰2017
class Single2 {
    private:
        Single2() { }
        Single2(const Single2&) = delete;
        Single2& operator=(const Single2&) = delete;
    public:
        static Single2& GetInst() 
        {
            static Single2 single;
            return single;
        }
};
\end{lstlisting}

上述版本的单例模式在C++11 以前存在多线程不安全的情况，编译器可能会初始化多个静态变量。

但是C++11推出以后，各厂商优化编译器，能保证线程安全。所以为了保证运行安全请确保使用C++11以上的标准。

这种方式存在一个很严重的问题，就是当多个线程都调用单例函数时，我们不确定资源是被哪个线程初始化的。回收指针存在问题，存在多重释放或者不知道哪个指针释放的问题。

所以，可以引入智能指针，来帮助我们进行内存管理。

\subsection{防止提前手动显式析构}
防止提前手动显式析构，可以把析构函数放到\verb|private|下，并且提供一个辅助类帮忙回收内存。

比如代码示例\ref{单例模式防止手动析构1}中，对于单例模式类\verb|SingleAutoSafe|，我们编写了一个\verb|SafeDeletor|类来负责回收他的内存，并且将\verb|SingleAutoSafe|的析构函数设定为私有\verb|private|。
\begin{code_example}\label{单例模式防止手动析构1}
\begin{lstlisting}
class SingleAutoSafe;
class SafeDeletor
{
    public:
    void operator()(SingleAutoSafe* sf)
    {
        std::cout << "this is safe deleter operator()" << std::endl;
        delete sf;
    }
};
class SingleAutoSafe
{
    private:
    SingleAutoSafe() {}
    ~SingleAutoSafe()
    {
        std::cout << "this is single auto safe deletor" << std::endl;
    }
    SingleAutoSafe(const SingleAutoSafe&) = delete;
    SingleAutoSafe& operator=(const SingleAutoSafe&) = delete;
    // 定义友元类，通过友元类调用该类析构函数
    friend class SafeDeletor;
    public:
    static std::shared_ptr<SingleAutoSafe> GetInst()
    {
        // 1处
        if (single != nullptr)
        {
            return single;
        }
        s_mutex.lock();
        // 2处
        if (single != nullptr)
        {
            s_mutex.unlock();
            return single;
        }
        // 额外指定删除器  
        // 3 处
        single = std::shared_ptr<SingleAutoSafe>(new SingleAutoSafe, SafeDeletor());
        // 也可以指定删除函数
        // single = std::shared_ptr<SingleAutoSafe>(new SingleAutoSafe, SafeDelFunc);
        s_mutex.unlock();
        return single;
    }
    private:
    static std::shared_ptr<SingleAutoSafe> single;
    static std::mutex s_mutex;
};
\end{lstlisting}
\end{code_example}

\subsection{(C++11)利用call\_once的一种单例模式实现方式}
C++11 提出了\verb|call_once|函数，我们可以配合一个局部的静态变量\verb|once_flag|实现线程安全的初始化。多线程调用\verb|call_once|函数时，会判断\verb|once_flag|是否被初始化，如没被初始化则进入初始化流程，调用我们提供的初始化函数。但是同一时刻只有一个线程能进入这个初始化函数。

删除了默认拷贝、拷贝构造和拷贝赋值。

由一个\verb|static|的智能指针来管理动态内存的单例模式的资源。
\begin{lstlisting}
// g++ singleton_pattern.cpp -lpthread -o singleton_pattern

#include <thread>
#include <memory>
#include <iostream>
#include <mutex>

// 博主: 恋恋风辰2017
class SingletonOnce {
private:
    SingletonOnce() = default;
    SingletonOnce(const SingletonOnce&) = delete;
    SingletonOnce& operator = (const SingletonOnce& st) = delete;
    static std::shared_ptr<SingletonOnce> _instance;
public:
    static std::shared_ptr<SingletonOnce> GetInstance() {
        static std::once_flag s_flag;
        std::call_once(s_flag, [&]() {
            _instance = std::shared_ptr<SingletonOnce>(new SingletonOnce);
        });
        return _instance;
    }
    void PrintAddress() {
        std::cout << _instance.get() << std::endl;
    }
    ~SingletonOnce() {
        std::cout << "this is singleton destruct" << std::endl;
    }
};
std::shared_ptr<SingletonOnce> SingletonOnce::_instance = nullptr;
\end{lstlisting}

测试过程如下。从结果可以看出，虽然运用了2个线程分别调用\verb|GetInstance()|创建了\verb|SingletonOnce|类的对象，但是实际上只创建了一个，因为地址是一样的。进程结束时，也只发生了一次析构。
\begin{lstlisting}
// 博主: 恋恋风辰2017
void TestSingle() {
    std::thread t1([]() {
        std::this_thread::sleep_for(std::chrono::seconds(1));
        SingletonOnce::GetInstance()->PrintAddress();    
    });
    std::thread t2([]() {
        std::this_thread::sleep_for(std::chrono::seconds(1));
        SingletonOnce::GetInstance()->PrintAddress();
    });
    t1.join();
    t2.join();
}

// 终端输出为
// 0x7fb668000b60
// 0x7fb668000b60
// this is singleton destruct
\end{lstlisting}

\subsection{单例模式的模板类}
为了让单例更加通用，可以做成模板类
\begin{lstlisting}
// 博主: 恋恋风辰2017
template <typename T>
class Singleton {
    protected:
        Singleton() = default;  // 删除了默认构造
        Singleton(const Singleton<T>&) = delete;  // 删除了拷贝构造
        Singleton& operator=(const Singleton<T>& st) = delete;  // 删除了拷贝赋值
        static std::shared_ptr<T> _instance;  // 静态的智能指针，指向一个对象
    public:
        static std::shared_ptr<T> GetInstance() {
            static std::once_flag s_flag;
            std::call_once(s_flag, [&]() { _instance = std::shared_ptr<T>(new T); });
            return _instance;
        }
        void PrintAddress() {
            std::cout << _instance.get() << std::endl;
        }
        ~Singleton() {
            std::cout << "this is singleton destruct" << std::endl;
        }
};
template <typename T>
std::shared_ptr<T> Singleton<T>::_instance = nullptr;
\end{lstlisting}

使用时，需要为模板类添加一个类型参数，可以是自己编写的类。这样，自己编写的类，就会被以单例模式的方式被管理起来了。在下面的代码实例中，创建了一个类\verb|Something|，然后在测试函数中，通过\verb|SingletonOnce<Something>|的方式，实例化了一个对象。从终端输出的地址来看，自己编写的类，以单例模式的方式被管理起来了。
\begin{lstlisting}
class Something {
    public:
    int m_a;
    float m_b;
};

// 博主: 恋恋风辰2017
void TestSingle() {
    std::thread t1([]() {
        std::this_thread::sleep_for(std::chrono::seconds(1));
        SingletonOnce<Something>::GetInstance()->PrintAddress();    
    });
    std::thread t2([]() {
        std::this_thread::sleep_for(std::chrono::seconds(1));
        SingletonOnce<Something>::GetInstance()->PrintAddress();
    });
    t1.join();
    t2.join();
}

// 终端输出为
// 0x7f2da8000b60
// 0x7f2da8000b60
// this is singleton destruct
\end{lstlisting}








\section{建造者模式(Builder Pattern)}

\subsection{含义}

使用多个简单的对象，一步一步的构建成一个复杂的对象。将一个复杂的构建，拆分成多个。这样，同一个构建过程，可以使用不同的实现。建造者模式(Builder Pattern)属于创建型模式，它提供了一种创建对象的最佳方式。

与工厂模式的\textbf{区别}是：建造者模式更加关注与零件装配的顺序

\subsection{解决的问题}

主要解决在软件系统中，有时候面临着"一个复杂对象"的创建工作，其通常由各个部分的子对象用一定的算法构成；由于需求的变化，这个复杂对象的各个部分经常面临着剧烈的变化，但是将它们组合在一起的算法却相对稳定

常使用于一些"基本部件不会变，而其组合经常变化"的时候

\subsection{优点}

\begin{itemize}
    \item 分离构建过程和表示，使得构建过程更加灵活，可以构建不同的表示
    \item 可以更好地控制构建过程，隐藏具体构建细节
    \item 代码复用性高，可以在不同的构建过程中重复使用相同的建造者
\end{itemize}

\subsection{缺点}

\begin{itemize}
    \item 如果产品的属性较少，建造者模式可能会导致代码冗余
    \item 建造者模式增加了系统的类和对象数量
\end{itemize}

\section{原型模式(Prototype Pattern)}

\subsection{含义}

用于创建重复的对象，同时又能保证性能

这种模式是实现了一个原型接口，该接口用于创建当前对象的克隆。当直接创建对象的代价比较大时，则采用这种模式。例如，一个对象需要在一个高代价的数据库操作之后被创建。我们可以缓存该对象，在下一个请求时返回它的克隆，在需要的时候更新数据库，以此来减少数据库调用

与通过对一个类进行实例化来构造新对象不同的是，原型模式是通过拷贝一个现有对象生成新对象的

\subsection{适用场景}

\begin{enumerate}
    \item 当一个系统应该独立于它的产品创建，构成和表示时
    \item 当要实例化的类是在运行时刻指定时，例如，通过动态装载
    \item 为了避免创建一个与产品类层次平行的工厂类层次时
    \item 当一个类的实例只能有几个不同状态组合中的一种时。建立相应数目的原型并克隆它们可能比每次用合适的状态手工实例化该类更方便一些
    \item 类初始化需要消化非常多的资源，这个资源包括数据、硬件资源等
    \item 通过\verb|new|产生一个对象需要非常繁琐的数据准备或访问权限，则可以使用原型模式
    \item 一个对象多个修改者的场景
    \item 一个对象需要提供给其他对象访问，而且各个调用者可能都需要修改其值时，可以考虑使用原型模式拷贝多个对象供调用者使用
\end{enumerate}

\chapter{结构型模式}

关注于对象的组成以及对象之间的依赖关系，如何将多个类组织成为一个更大的结构

\begin{enumerate}
    \item 适配器模式(Adapter Pattern)
    \item 装饰者模式(Decorator Pattern)
    \item 代理模式(Proxy Pattern)
    \item 外观模式(Facade Pattern)
    \item 桥接模式(Bridge Pattern)
    \item 组合模式(Composite Pattern)
    \item 享元模式(Flyweight Pattern)
\end{enumerate}

\section{适配器模式(Adapter Pattern)}

\subsection{含义}

将一个类的接口，转变为所需求的另外一个接口，使得原本不可以一起工作的那些类可以一起工作

适配器继承或依赖已有的对象，实现想要的目标接口

注意，适配器不是在前期设计时添加的，而是解决正在服役的项目的问题

\subsection{优点}

\begin{enumerate}
    \item 可以让任何两个没有关联的类一起运行
    \item 提高了类的复用
    \item 增加了类的透明度
    \item 灵活性好
\end{enumerate}

\subsection{缺点}

过多地使用适配器，会让系统非常零乱，不易整体进行把握。比如，明明看到调用的是 A 接口，其实内部被适配成了 B 接口的实现，一个系统如果太多出现这种情况，无异于一场灾难。因此如果不是很有必要，可以不使用适配器，而是直接对系统进行重构

\section{装饰者模式(Decorator Pattern)}

\subsection{含义}

允许向一个现有的对象增加新的功能，同时又不改变其原结构

动态地给一个对象增加一些额外的职责，装饰器模式比生成子类更加灵活

未使用装饰者模式，如果A类别有2个元素，B类别有3个元素，彼此组合形成6种组合方式。如果增加新的元素，例如变成2*4或者3*3的形式，势必增大代码量

\paragraph{装饰模式”和“代理模式”有啥区别？}

两者都是对类的方法进行扩展，但装饰器模式强调的是增强自身，在被装饰之后你能够在被增强的类上使用增强后的功能。增强后你还是你，只不过能力更强了而已；而代理模式则强调要让别人帮你去做一些本身与你业务没有太多关系的职责(记录日志、设置缓存)。代理模式是为了实现对象的控制，因为被代理的对象往往难以直接获得或者是其内部不想暴露出来

装饰器模式应当为所装饰的对象提供增强功能，而代理模式对所代理对象的使用施加控制，并不提供对象本身的增强功能

\subsection{优点}

\begin{enumerate}
    \item 装饰类和被装饰类可以独立发展，不会相互耦合，互相都不用知道对方的存在
    \item 装饰模式是继承的一个替代模式，装饰模式可以动态扩展一个实现类的功能，无论包装多少层，返回的对象都是is-a的关系
    \item 实现动态扩展，只要继承了装饰器就可以动态扩展想要的功能了
\end{enumerate}

\subsection{缺点}

多层装饰比较复杂，提高了系统的复杂度。不利于我们调试

\subsection{适用场景}

在不影响其它对象的情况下，以动态、透明的方式给单个对象添加职责

处理那些可以撤销的职责

当使用子类扩展的方式不切实际的时候，可考虑使用装饰器模式

\subsection{组成}

\begin{enumerate}
    \item Component(抽象组件)：它是具体构件和抽象装饰类的共同父类，声明了在具体构件中实现的业务方法，它的引入可以使客户端以一致的方式处理未被装饰的对象以及装饰之后的对象，实现客户端的透明操作
    \item ConcreteComponent(具体组件)：它是抽象构件类的子类，用于定义具体的构件对象，实现了在抽象构件中声明的方法，装饰器可以给它增加额外的职责(方法)
    \item Decorator(抽象装饰类)：它也是抽象构件类的子类，用于给具体构件增加职责，但是具体职责在其子类中实现。它维护一个指向抽象构件对象的引用，通过该引用可以调用装饰之前构件对象的方法，并通过其子类扩展该方法，以达到装饰的目的
    \item ConcreteDecorator(具体装饰类)：它是抽象装饰类的子类，负责向构件添加新的职责。每一个具体装饰类都定义了一些新的行为，它可以调用在抽象装饰类中定义的方法，并可以增加新的方法用以扩充对象的行为
\end{enumerate}

\section{代理模式(Proxy Pattern)}

为其他对象提供一种代理，以控制对这个对象的访问

买火车票不一定在火车站买，也可以去代售点买

与适配器模式的区别，代理模式并不改变接口

与装饰器模式的区别，装饰器模式是为了增加新功能，而代理模式只是为了加以控制

\subsection{优点}

\begin{enumerate}
    \item 职责清晰：真实的角色就是实现实际的业务逻辑，不用关心其他非本职的事务，通过后期的代理来完成事务，附带的结果就是编程简洁清晰
    \item 代理对象可以在客户端和目标对象之间起到中介的作用，保护了目标对象
    \item 高扩展性
\end{enumerate}

\subsection{组成}

\begin{enumerate}
    \item 抽象角色：通过接口或抽象类声明真实角色实现的业务方法
    \item 真实角色：实现抽象角色，定义真实角色所要实现的业务逻辑，供代理角色调用
    \item 代理角色：实现抽象角色，是真实角色的代理，通过真实角色的业务逻辑方法来实现抽象方法，并可以附加自己的操作。(访问权限)
\end{enumerate}

\section{外观模式(Facade Pattern)}

隐藏系统的复杂一面，并提供了一个客户端可以访问系统的接口。一个负责管理的类，由他来简化“操作其他许多的类”这一项复杂的工作

例如，去医院看病，挂号、门诊、检查、取药等操作十分复杂，如果有一个接待员，让接待人员来辅助处理，则会简化许多

外观模式是最少知道原则法则的典型应用

通常可以将外观对象设计成单例模式

\paragraph{外观模式和抽象工厂模式}

外观类可以通过抽象工厂获取子系统的实例，子系统可以从内部对外观类进行屏蔽

\paragraph{外观模式和中介模式}

外观模式关注的是外界与子系统间的交互，而中介者模式关注的是子系统内部间的交互.中介模式尚未研究到，暂时不深入

\subsection{优点}

\begin{enumerate}
    \item 降低了子系统与客户端之间的耦合，减少了系统的相互依赖，使得子系统的变化不会影响调用它的客户类
    \item 提高了灵活性。不管系统内部如何变化，只要不影响到外观对象，任你自由活动
    \item 提高了安全性。想让你访问子系统的哪些业务就开通哪些逻辑，不在外观上开通的方法，你就访问不到
\end{enumerate}

\subsection{缺点}

\begin{enumerate}
    \item 增加新的子系统可能需要修改外观类或客户端的源码，违背了开闭原则
\end{enumerate}

\subsection{组成}

\begin{enumerate}
    \item 外观角色：为多个子系统对外提供一个共同的接口
    \item 子系统角色：实现系统的部分功能，客户可以通过外观角色访问他
    \item 客户角色：通过一个外观角色访问各个子系统的功能
\end{enumerate}

\section{桥接模式(Bridge Pattern)}

将抽象化和实现化解耦。

又称为handler and body模式或者interface模式

\subsection{优点}

\begin{enumerate}
    \item 分离抽象类及其实现部分。桥接模式使用“对象间的关联关系”解耦了抽象和实现之间 固有的绑定关系，使得抽象和实现可以沿着各自的维度来变化
    \item 桥接模式是比多继承方案更好的解决方法
    \item 桥接模式提高了系统的可扩展性，在两个变化维度中任意扩展一个维度，都不需要修改原有的系统
    \item 实现细节对客户透明，可以对用户隐藏实现细节。用户在使用时不需要关系实现，在抽象层通过聚合关联关系完成封装与对象组合
\end{enumerate}

\subsection{缺点}

\begin{enumerate}
    \item 桥接模式要求正确识别出系统的两个独立变化的维度，因此使用范围具有一定的局限性
\end{enumerate}

\subsection{适用场景}

\begin{enumerate}
    \item 当一个类存在两个独立变化的维度，且这两个维度都需要进行扩展时
    \item 当一个系统不希望使用继承或因为多层次继承导致系统类的个数急剧增加时
    \item 当一个系统需要在构件的抽象化角色和具体化角色之间增加更多的灵活性时
\end{enumerate}

\section{组合模式(Composite Pattern)}

将对象组合成树的形式

容器对象和叶子对象之间，存在区别，也存在差异

举例

水果盘里有各种水果，苹果、香蕉、梨子等，大水果盘里面还有可能有小水果盘。现在要吃水果。

抽象的部件类MyElement，描述全部部件共有的行为，比如“可以被吃”，“吃掉里面所有的水果”，“摆放(到一个果盘中)”，“(从一个果盘中)移出”

Apple，Banana，Pear都继承自MyElement

容器类PlateComposite也继承自MyElement

\subsection{分类}

\textbf{透明组合模式}和\textbf{安全组合模式}

\subsection{组成}

\begin{enumerate}
    \item Component：抽象的组件对象(抽象构件)。抽象构件可以是接口或抽象类，为叶子构件和容器构件对象声明接口，在该角色中可以包含所有子类共有的声明和实现。在抽象构件中定义了访问及管理它的子构件的方法，如增加子构件、删除子构件、获取子构件等
    \item Leaf：叶子节点对象(叶子构件)。定义和实现叶子对象的行为，不再包含其他的子节点对象。叶子构件在组合结构中表示叶子节点对象，叶子节点没有子节点，它实现了在抽象构件中定义的行为
    \item Composite：组合对象(容器构件)。容器构件在组合结构中表示容器节点对象，容器节点包含子节点，其子节点可以是叶子节点，也可以是容器节点，它提供了一个集合用于存储子节点，它实现了在抽象构件中定义的行为，包含那些访问及管理子构件的方法
    \item Client：客户端。客户类可以通过抽象构件接口访问和控制组合构件中的对象
\end{enumerate}

\section{享元模式(Flyweight Pattern)}

主要用于减少创建对象的数量，减少内存占用，提高性能

尝试重用现有的同类对象，如果未找到匹配的对象，则创建新对象

享元模式摒弃了在每个对象中保存所有数据的方式，运用共享技术，共享多个对象所共有的相同状态， 能在有限的内存容量中载入更多对象，以支持存在大量细粒度对象且内存不足的场景

享元模式只是一种优化。在应用该模式之前，需要确定程序中存在与大量类似对象同时占用内存相关的内存消耗问题，并且确保该问题无法使用其他更好的方式来解决

\subsection{适用场景}

\begin{enumerate}
    \item 仅在程序必须支持大量对象且没有足够的内存容量时使用享元模式
    \item 程序需要生成数量巨大的相似对象
    \item 目标内存不足
    \item 对象中包含可抽取且能在多个对象间共享的重复状态
\end{enumerate}

\subsection{组成}

\begin{enumerate}
    \item 享元(Flyweight)：类包含原始对象中部分能在多个对象中共享的状态。同一享元对象可在许多不同情景中使用。享元中存储的状态被称为 “内在状态”。传递给享元方法的状态被称为 “外在状态”
    \item 情景(Context)：类包含原始对象中各不相同的外在状态。情景与享元对象组合在一起就能表示原始对象的全部状态。通常情况下，原始对象的行为会保留在享元类中。因此调用享元方法必须提供部分外在状态作为参数。但也可将行为移动到情景类中，然后将连入的享元作为单纯的数据对象
    \item 客户端(Client)：负责计算或存储享元的外在状态。在客户端看来，享元是一种可在运行时进行配置的模板对象，具体的配置方式为向其方法中传入一些情景数据参数
    \item 享元工厂(Flyweight Factory)：会对已有享元的缓存池进行管理。有了工厂后，客户端就无需直接创建享元，它们只需调用工厂并向其传递目标享元的一些内在状态即可。工厂会根据参数在之前已创建的享元中进行查找，如果找到满足条件的享元就将其返回；如果没有找到就根据参数新建享元
\end{enumerate}

\chapter{行为型模式}

\subsection{简介}

关注于对象的行为问题，是在不同对象之间划分责任和算法的抽象化

这些设计模式特别关注模块之间的通信，行为型模式用于描述程序在运行时复杂的流程控制，即描述多个类或对象之间怎样相互协作共同完成单个对象都无法单独完成的任务，它涉及算法与对象间职责的分配

行为型模式更侧重实现一个具体而精巧的功能，结构性模式更侧重模块与模块之间关系，关注类和对象的组合是否体现了修改关闭，扩展开放的原则

\begin{enumerate}
    \item 策略模式(Strategy Pattern)
    \item 模板方法模式(Template Pattern)
    \item 观察者模式(Observer Pattern)
    \item 迭代器模式(Iterator Pattern)
    \item 责任链模式(Chain of Responsibility Pattern)
    \item 命令模式(Command Pattern)
    \item 备忘录模式(Memento Pattern)
    \item 状态模式(State Pattern)
    \item 访问者模式(Visitor Pattern)
    \item 中介者模式(Mediator Pattern)
    \item 解释器模式(Interpreter Pattern)
\end{enumerate}

\section{策略模式(Strategy Pattern)}

\subsection{含义}

定义一系列的算法，将之封装起来，使他们可以相互替换

主要解决，在有多种算法相似的情况下，使用 if...else 所带来的复杂和难以维护

注意，如果一个系统的策略多于四个，就需要考虑使用混合模式，解决策略类膨胀的问题

\paragraph{策略模式和工厂模式的区别}

\begin{enumerate}
    \item 用途不一样
    
    厂模式是创建型模式，它的作用是创建对象；而策略模式是行为型模式，它的作用是让一个对象在许多行为中选择一种行为
    
    \item 关注点不一样
    
    工厂模式关注对象创建；策略模式关注行为的封装；
    
    \item 解决不同的问题
    
    工厂模式是创建型的设计模式，它接受指令，创建出符合要求的实例；它主要解决的是资源的统一分发，将对象的创建完全独立出来，让对象的创建和具体的使用客户无关。主要应用在多数据库选择，类库文件加载等。
    
    策略模式是为了解决的是策略的切换与扩展，更简洁的说是定义策略族，分别封装起来，让他们之间可以相互替换，策略模式让策略的变化独立于使用策略的客户。
\end{enumerate}

\subsection{优点}

算法可以自由切换。避免使用多重条件判断。扩展性良好。

\subsection{缺点}

策略类会增多。所有策略类都需要对外暴露。

\subsection{适用场景}

\begin{enumerate}
    \item 如果在一个系统里面有许多类，它们之间的区别仅在于它们的行为，那么使用策略模式可以动态地让一个对象在许多行为中选择一种行为
    \item 一个系统需要动态地在几种算法中选择一种
    \item 如果一个对象有很多的行为，如果不用恰当的模式，这些行为就只好使用多重的条件选择语句来实现
\end{enumerate}

\subsection{组成}

\begin{enumerate}
    \item 环境(Context)：维护一个对策略对象的引用，负责将客户端请求委派给具体的策略对象执行。环境类可以通过依赖注入、简单工厂等方式来获取具体策略对象
    \item 抽象策略(Abstract Strategy)：定义了策略对象的公共接口或抽象类，规定了具体策略类必须实现的方法
    \item 具体策略(Concrete Strategy)：实现了抽象策略定义的接口或抽象类，包含了具体的算法实现
\end{enumerate}

策略模式中的上下文环境(Context)，其职责是隔离客户端与策略类的耦合，让客户端完全与上下文环境沟通，无需关系具体策略。

为了避免客户端内部直接自己指定要哪种策略而造成客户端与具体策略类耦合，一般可以通过简单工厂模式将具体策略的创建与客户端进行隔离，或者是通过策略枚举将上下文环境与具体策略类融合在一起，简化代码。当具体策略相对稳定时，推荐使用策略枚举简化代码

\section{模板方法模式(Template Pattern)}

定义一个操作中的算法的框架，而将一些步骤延迟到子类中

可以不改变一个算法的结构即可重定义该算法的某些特定步骤

\textbf{意图}：定义一个操作中的算法的骨架，而将一些步骤延迟到子类中。模板方法使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。

\textbf{主要解决}：一些方法通用，却在每一个子类都重新写了这一方法。

\textbf{何时使用}：有一些通用的方法。

\textbf{如何解决}：将这些通用算法抽象出来。

\textbf{关键代码}：在抽象类实现，其他步骤在子类实现。

\begin{enumerate}
    \item 基本方法：基本方法也叫做基本操作，是由子类实现的方法，并且在模板方法被调用
    \item 模板方法：核心方法，不允许子类重写，所以都会加上final修饰符，可以有一个或几个，一般是一个具体方法框架，按照固定的流程对基本方法的调度
    \item 钩子方法：为了让模板方法的执行结果的更好地适应因外界条件改变。
\end{enumerate}

比如说银行办理业务为例，办理业务是个模板方法，普通人要经历排队、取号、等待、办理四个基本流程，而Vip则不需要排队、取号、等待，那么在设计的时候我们的抽象模板类需要考虑到，于是乎你得需要在模板方法各基本方法调用之前增加条件判断，那么用于设置这个条件的方法就是钩子方法(Hook Method)，钩子方法也可以是抽象的还可以由子类的一个方法返回值决定公共部分的执行结果

\subsection{组成}

\begin{enumerate}
    \item AbstractClass：是抽象类，其实也就是一个抽象模板，定义并实现了一个模板方法。这个模板方法一般是一个具体方法，它给出了一个顶级逻辑的框架，而逻辑的组成步骤在相应的抽象操作中，推迟到子类实现。顶级逻辑也有可能调用一些具体方法
    \item ConcreteClass：实现父类所定义的一个或多个抽象方法。每一个AbstractClass都可以有任意多个ConcreteClass与之对应，而每一个ConcreteClass都可以给出这些抽象方法(也就是顶级逻辑的组成步骤)的不同实现，从而使得顶级逻辑的实现各不相同
\end{enumerate}

当不变的和可变的行为在方法的子类实现中混合在一起的时候，不变的行为就会在子类中重复出现。我们通过模板方法模式，把这些行为搬移到单一的地方，这样帮助子类摆脱重复的不变行为的纠缠

\subsection{优点}

良好的扩展性，封装不变部分，扩展可变部分，把认为是不变部分的算法封装到父类实现，而可变部分的则可以通过继承来继续扩展。例如增加一个新的功能很简单，只要再增加一个子类，实现父类的基本方法就可以了。

提取公共部分代码，便于维护，减小维护升级成本，基本操作由父类定义，子类实现

基本方法是由子类实现的，因此子类可以通过扩展的方式增加相应的功能，符合开闭原则

\subsection{缺点}

通常抽象类是负责声明某一类的事物的共同属性和抽象方法，实现类则是完成定义具体的特性和方法。但是模板方法模式却颠倒了，抽象类定义了部分抽象方法，由子类实现，子类执行的结果影响了父类的结果，也就是子类对父类产生了影响。每个不同的实现都需要定义一个子类，这会导致类的个数增加，系统更加庞大，设计也更加抽象，但是更加符合“单一职责原则”，使得类的内聚性得以提高

\subsection{适用场景}

\begin{enumerate}
    \item 多个子类有公有的方法，并且逻辑基本相同时
    \item 重要、复杂的算法，可以把核心算法设计为模板方法，周边的相关细节功能则由各个子类实现
    \item 重构时，模板方法模式是一个经常使用的模式，把相同的代码抽取到父类中，然后通过钩子方法约束其行为
\end{enumerate}

\section{观察者模式(Observer Pattern)}
\paragraph{基本概念}
当对象存在一对多关系时，则使用观察者模式。

是一种行为型模型。

当一个对象的状态发生改变时，所有依赖于它的对象都将得到通知并自动更新。

要考虑到易用和低耦合，保证高度的协作。

\paragraph{观察}
观察，并不需要持续性地查询被观察者的状态。而是采用注册，或者订阅的方式，来被动地获取被观察者的状态变化。省去了反复查询的资源消耗。

\paragraph{适用场景}
\begin{enumerate}
    \item 一个抽象模型有两个方面，其中一个方面依赖于另一个方面。将这些方面封装在独立的对象中使它们可以各自独立地改变和复用
    \item 一个对象的改变将导致其他一个或多个对象也发生改变，而不知道具体有多少对象将发生改变，可以降低对象之间的耦合度
    \item 一个对象必须通知其他对象，而并不知道这些对象是谁
    \item 需要在系统中创建一个触发链，A对象的行为将影响B对象，B对象的行为将影响C对象，可以使用观察者模式创建一种链式触发机制
\end{enumerate}

\section{迭代器模式(Iterator Pattern)}

用于顺序访问集合对象的元素，不需要知道集合对象的底层表示

\section{责任链模式(Chain of Responsibility Pattern)}
为请求创建了一个由接收者构成的链，如果当前的接收者并不能处理该请求，则将该请求传递给下一个接收者，以此类推。

责任链设计模式和链表非常相似。

\subsection{适用场景举例}
责任链模式应用的背景，往往是，\textbf{业务具有顺序性，是依次执行的特点}。假如有业务A-G，执行的时候先执行业务A，业务A如果执行返回成功，则执行业务B，以此类推。如果业务的执行返回失败，则后面的也统统不需要执行了。这种场景下，就很适合责任链设计模式。

比如游戏的关卡，一定是通关了第1关，才能进入第2关。当然游戏关卡这个场景，并没有"业务返回失败后面所有业务都不需要执行"这一条需求的。

从另一个角度来说，\textbf{一个完整的大的业务，被划分为了有顺序的多个小的业务}。而且这些小的任务，具有\verb|AND|的运算关系，即必须全部都执行成功，才能说明大的业务成功。比如用户登录的过程，必须用户名，密码，验证码，还有其他信息全部都通过服务器的核验才能完成登录。

\subsection{基本概念}
\begin{itemize}
    \item 客户端发出一个请求，链上的对象都有机会来处理这一请求，而客户端不需要知道谁是具体的处理对象。客户端负责组装链式结构，但是客户端不需要关心最终是谁来处理了任务。
    \item 多个对象都有机会处理请求，从而\textbf{避免了请求的发送者和接受者之间的耦合关系}。
    \item 将这些对象连成一条链，并沿着这条链传递该请求，直到有对象处理它为止。其过程实际上是一个递归调用。
\end{itemize}

\subsection{初步实现责任链模式}
为每一个游戏关卡，都写一个类，并且在每个类中留一个指向下一个类的接口。

这样做的缺陷是，代码的复用性比较差，每个关卡类之间的差异其实不大，但是有大量需要人工拷贝的代码，这就为调试与升级带来了隐患。

改进方式是，抽象出一个关卡类，让所有的关卡类来复用他，并且能够增加新的功能。

\subsection{基本组成}
\begin{enumerate}
    \item 抽象处理者(Abstract Handler)：提供对下一个处理者的接口，提供对具体处理者的调用接口
    \item 具体处理者(Concrete Handler)：负责业务的具体实现，但是是从抽象处理者得到指示，向他返回结果。
    \item 责任链客户端(Handler Client)：负责生成整个处理者链，并且返回第一个抽象处理者。业务到达本端之后，就由责任链客户端来负责将任务交给链条上的每个节点进行处理。
\end{enumerate}

\subsection{优点}
\begin{enumerate}
    \item 动态组合，使请求者和接受者解耦。
    \item 请求者和接受者松散耦合：\textbf{请求者不需要知道接受者，也不需要知道如何处理}。每个职责对象只负责自己的职责范围，其他的交给后继者。各个组件间完全解耦。
    \item 动态组合职责：职责链模式会把功能分散到单独的职责对象中，然后\textbf{在使用时动态的组合形成链}，从而可以灵活的分配职责对象，也可以灵活的添加改变对象职责。
\end{enumerate}

\subsection{缺点}
\begin{enumerate}
    \item \textbf{产生很多细粒度的对象}：因为功能处理都分散到了单独的职责对象中，每个对象功能单一，要把整个流程处理完，需要很多的职责对象，会产生大量的细粒度职责对象。
    \item 不一定能处理：每个职责对象都只负责自己的部分，这样就可以出现某个请求，即使把整个链走完，都没有职责对象处理它。这就\textbf{需要提供默认处理}，并且注意构造链的有效性。
\end{enumerate}

\subsection{代码示例 - 办公系统的请假审批}
在代码示例\ref{责任链模式代码示例_办公系统的请假审批}中，责任链由低到高分别由\verb|manager|,\verb|ceo|和\verb|boss|组成。

\begin{code_example}\label{责任链模式代码示例_办公系统的请假审批}
\begin{lstlisting}
#include <iostream>
using namespace std;

enum class RequestType :char {qingjia,zhangxin,cizhi};
//抽象管理者类，manager，ceo，boss都继承这个类
class AbstractManager
{
    public:
    void setNext(AbstractManager* next) //指向下一个管理者
    {
        m_next = next;
    }
    virtual void  handleRequest(RequestType type) = 0; //每个管理者都要重写这个处理请求的函数
    virtual ~AbstractManager() {}
    protected:
    AbstractManager* m_next = nullptr; //子类可以继承
};

//manager主管
class Manager : public AbstractManager
{
    public:
    void  handleRequest(RequestType type)  //每个管理者都要重写这个处理请求的函数
    {
        switch (type)
        {
            case RequestType::qingjia:
            cout << "请假：同意" << endl;
            break;
            case RequestType::zhangxin:
            cout << "涨薪:我要请示CEO" << endl;
            m_next->handleRequest(type); //m_next是下个界别的对象指针
            break;
            case RequestType::cizhi:
            cout << "辞职：我要请示上级" << endl;
            m_next->handleRequest(type); //m_next是下个界别的对象指针
            break;
            default:
            break;
        }
    }
};

//CEO
class CEO : public AbstractManager
{
    public:
    void  handleRequest(RequestType type)  //每个管理者都要重写这个处理请求的函数
    {
        switch (type)
        {
            case RequestType::qingjia:
            cout << "请假：同意" << endl;
            break;
            case RequestType::zhangxin:
            cout << "再等等吧" << endl;
            break;
            case RequestType::cizhi:
            cout << "辞职：我要请示上级" << endl;
            m_next->handleRequest(type); //m_next是下个界别的对象指针
            break;
            default:
            break;
        }
    }
};

//boss
class BOSS : public AbstractManager
{
    public:
    void  handleRequest(RequestType type)  //每个管理者都要重写这个处理请求的函数
    {
        switch (type)
        {
            case RequestType::qingjia:
            cout << "请假：再忍忍吧" << endl;
            break;
            case RequestType::zhangxin:
            cout << "再等等吧" << endl;
            break;
            case RequestType::cizhi:
            cout << "辞职：再缓一缓吧" << endl;
            break;
            default:
            break;
        }
    }
};

//请假人的类
class YG
{
    public:
    void request(RequestType type, AbstractManager* manager)
    {
        manager->handleRequest(type);
    }
};

int main()
{
    Manager* manager = new Manager;
    CEO* ceo = new CEO;
    BOSS* boss = new BOSS;
    
    manager->setNext(ceo);
    ceo->setNext(boss);
    
    YG* yg = new YG;
    yg->request(RequestType::qingjia, manager);
    yg->request(RequestType::zhangxin, manager);
    yg->request(RequestType::cizhi, manager);
    
    yg->request(RequestType::qingjia, ceo);
    yg->request(RequestType::zhangxin, ceo);
    yg->request(RequestType::cizhi, ceo);
    
    yg->request(RequestType::qingjia, boss);
    yg->request(RequestType::zhangxin, boss);
    yg->request(RequestType::cizhi, boss);
    
    return 0;
    
}    
\end{lstlisting}
\end{code_example}
程序的输出为

\begin{lstlisting}
请假：同意
涨薪:我要请示CEO
再等等吧
辞职：我要请示上级
辞职：我要请示上级
辞职：再缓一缓吧
请假：同意
再等等吧
辞职：我要请示上级
辞职：再缓一缓吧
请假：再忍忍吧
再等等吧
辞职：再缓一缓吧
\end{lstlisting}

\section{命令模式(Command Pattern)}

将请求以命令的形式包裹在对象中，并传递给调用对象。调用对象寻找可以处理该命令的对象，并将该命令传递给该对象

\section{备忘录模式(Memento Pattern)}

保存一个对象的状态，以便在适当的时候恢复对象

\section{状态模式(State Pattern)}

类的行为是基于它的状态改变的

\section{访问者模式(Visitor Pattern)}

将数据结构与数据操作分离

解决稳定的数据结构和易变的操作耦合问题

\section{中介者模式(Mediator Pattern)}

降低多个对象和类之间的通信复杂性

提供一个中介类，处理不同类之间的通信，支持松耦合，使得代码易于维护

用中介对象来封装对象交互，中介者使得各对象不需要显式地相互引用

\section{解释器模式(Interpreter Pattern)}

实现了一个表达式接口，解释一个特定的上下文

给定一种编程语言，定义它的文法表示，并定义一个解释器，这个解释器使用该标识来解释语言中的句子

应用场景是，编译器，运算表达式计算

\part{C++开源工具}
\chapter{gdb - 调试工具}
\section{什么是coredump}
Coredump叫做核心转储，它是进程运行时在突然崩溃的那一刻的一个内存快照。操作系统在程序发生异常而异常在进程内部又没有被捕获的情况下，会把进程此刻内存、寄存器状态、运行堆栈等信息转储保存在一个文件里。

该文件也是二进制文件，可以使用gdb、elfdump、objdump或者windows下的windebug、solaris下的mdb进行打开分析里面的具体内容。

调试coredump文件命令

\begin{lstlisting}
gdb 程序名 core文件名    
\end{lstlisting}

\section{常用命令}
\subsection{运行}
\verb|c|，继续运行，直至下一个断点

\verb|s|，执行当前语句，如果该语句为函数调用，则进入该函数。但是前提是要有源代码，如果只有库文件是不行的。

\verb|n|，下一行，不进入函数调用。

\subsection{查询与设置变量的值}
\verb|p|显示变量的值，比如\verb|p f_flag|

\verb|p argc|，\verb|p argv|，显示主程序的命令行参数

\verb|set var name=value|设置变量的值，比如\verb|set var i=1|

\verb|set args|设置主程序的参数

如果包含特殊字符，可以用双引号括起来

\subsection{设置断点 - break, b(breakpoint)}
设置断点的基本命令\verb|break [filename]:[line]|，简写为\verb|b|

运行程序\verb|r|

\verb|b [functionName]|，为某个函数设置断点，只要代码中调用该函数，就会命中并暂停。如果函数不存在，gdb会给出提示。

如果有重名函数(函数重载，继承)，那么所有函数都会被命中并暂停。如果想特别地在某个版本上加断点，则必须给出更多信息，\verb|b test_1::test_fun|或者\verb|b test_fun(int)|

\paragraph{查询当前断点设置情况}\verb|info b|

\paragraph{使用正则表达式设置函数断点}\verb|rbreak 正则表达式|，简写为\verb|rb|。举例\verb|rb test_fun*|会为所有以\verb|test_fun|开头的函数都加上断点。

\paragraph{通过偏移量设置断点}\verb|b +[offset]|或者\verb|b -[offset]|，\verb|+|号表示向前，向着代码还未执行的方向，\verb|-|号表示向后，向着代码已经执行了的方向。

\paragraph{设置条件断点}\verb|b 断点 条件|，满足某个给定条件时才会命中增加断点。条件，指的是\verb|bool|表达式，比如\verb|if i == 5|。断点，既可以是文件名，也可以是函数名。比如\verb|b fun_test if a==10|，意思是希望在调用 \verb|fun_test| 函数并且参数 a 等于 10 时，程序暂停。

\paragraph{在指令地址上设置断点}\verb|b *指令地址|，可以先用\verb|p functionName|获取某个符号的地址，再用命令在该地址上标注断点。

\paragraph{设置临时断点}\verb|tbreak 断点|，缩写为\verb|tb|

\subsection{显示源代码 - list}
可以打印出所调试程序的源代码，当然，在程序编译时一定要加上\verb|-g|的参数，把源程序信息编译到执行文件中。

\begin{itemize}
    \item \verb|list <linenum>|显示程序第linenum行的周围的源程序。
    \item \verb|list <filename:linenum>|显示指定个文件的指定一行
    \item \verb|list <function>|显示函数名为function的函数的源程序。
    \item \verb|list <filename:function>|显示指定个文件的指定函数
    \item \verb|list <*address>|显示程序运行时的语句在内存中的地址
    \item \verb|list|显示当前行后面的源程序。
    \item \verb|list -offset|显示当前行前面的源程序。\verb|set listsize <count>|设置一次显示源代码的行数。\verb|show listsize|查看当前listsize的设置。    
    \item \verb|list <first>, <last>|显示从first行到last行之间的源代码。
    \item \verb|list , <last>|显示从当前行到last行之间的源代码。
    \item \verb|list +|往后显示源代码。
\end{itemize}

\subsection{查看信息 - info}
基本格式\verb|info [子命令]|

结合\verb|grep|和管道符共同使用

\begin{itemize}
    \item \verb|info breakpoints|查看断点信息
    \item \verb|info locals|查看局部变量
    \item \verb|info registers|查看寄存器信息
    \item \verb|info set|显示所有GDB设置
    \item \verb|info source|有关当前源文件的信息
    \item \verb|info sources|程序中的所有源文件或匹配的源文件
    \item \verb|info threads|显示当前已知的线程
    \item \verb|info vtbl|显示C++对象的虚函数表
\end{itemize}


\section{调试}


\subsection{调试正在运行的程序}
ps –ef查看进程编号，前面的数字是进程的编号

\begin{lstlisting}
gdb 程序名 –p 进程号
\end{lstlisting}

进入gdb时，程序会暂停

n运行下一步

\verb|gdb attach [进程号]|，调试正在运行的程序。

\subsection{查看堆栈信息}
bt是backtrace的缩写，作用是打印当前的函数调用栈的所有信息。

\begin{lstlisting}
(gdb) bt
#0 func (n=250) at tst.c:6
#1 0x08048524 in main (argc=1, argv=0xbffff674) at tst.c:30
#2 0x40040Arrayed in __libc_start_main () from /lib/libc.so.6    
\end{lstlisting}

由上面的代码示例，可知其函数的调用栈信息为\verb|__libc_start_main --> main() --> func()|

\verb|#0|是栈顶，\verb|#n|是栈底，下方的函数调用了上方的函数。

一般来说，程序停止时，最顶层的栈就是当前栈。假如函数A里面调用了函数B，则A应该在B的下方，A更靠近栈底，B更靠近栈顶。栈遵循"先进后出"的规则。

\subsection{调试多进程}
调试父进程\verb|set follow-fork-mode parent(默认值)|

调试子进程\verb|set follow-fork-mode child|

设置调试模式\verb|set detach-on-fork [on|off]|(默认值是on)，如果为on，则调试当前进程时，其他进程继续运行，如果为off，则调试当前进程时，其他进程被gdb挂起。

查看调试的进程\verb|info inferiors|

切换当前调试的进程\verb|inferior 进程id|，进程id指的是运行info

运行\verb|inferiors|时显示的第一列的值，从0开始的正整数，不是ps –ef查询的进程号)

\subsection{调试多线程程序}
查看当前运行的轻量级线程\verb|ps –aL | grep 程序名|

查看主线程和新线程之间的关系\verb|pstree –p 主线程id|

查看线程\verb|info threads|

切换线程\verb|thread 线程id|

只运行当前的线程\verb|set scheduler-locking on|

运行全部的线程\verb|set scheduler-locking off|

指定某线程运行某个gdb命令\verb|thread apply 线程id 命令|

全部线程运行某个gdb命令\verb|thread apply all 命令|

\chapter{gtest - 谷歌测试框架}
主要针对单元测试(unit test)，但是也能兼顾其他类型的测试任务。

\section{gtest的设计思路}
gtest对测试的看法，以及其设计思路
\begin{enumerate}
    \item 测试任务之间应当是独立的关系，并且具有可重复的能力。一旦某一点的测试任务失败了，gtest可以单独运行失败的这一部分的测试任务，而无须重复运行相关联的成功了的测试任务。
    \item 测试任务应当有一个良好的组织，并且能够反映原来工程的结构。gtest将测试任务组织成测试套件(test suites)，他们之间可以共享数据和子例程(subroutine)。
    \item 测试任务必须可移植、可重用。gtest提供了许多与平台无关的代码，以提供可移植和可重用的能力。
    \item 测试一旦失败，应当尽可能多的提供信息。测试发生失败时，gtest可以停止整个测试的运行，也可以不停止，这可以由开发人员选择。
    \item gtest负责了一些杂活，比如测试的运行顺序、次数等，减轻开发人员的工作量。
    \item 测试应当尽可能地快。
\end{enumerate}

\subsection{测试套件(test suite)}
测试，指的是我们用一系列的断言，来验证某部分代码是否合格。

\paragraph{测试套件(test suite)}
我们将一系列相关联的测试，统称为测试套件(test suite)，他们具有相关性，而且能够反映源代码的结构。

\subsection{测试夹具(test fixture)}
如果测试套件中，有些测试可能要共享数据和子例程，那么可以把它们放到测试夹具(test fixture)类中。

一个测试程序，包含一个或者多个测试套件。

\section{断言(assertion)}
\subsubsection{什么是断言}
断言(assertion)，指的是一个用于判断\verb|true|或者\verb|false|的语句。

\subsubsection{断言的结果}
断言的结果，有3种情况，成功(success)，致命错误(fatal failure)，非致命错误(non-fatal failure)。发生致命错误时，程序会退出当前的调用，而其他2种情况并不会退出该调用。

\subsubsection{gtest中的断言}
gtest中的断言，往往以宏定义的形式存在，或者类似于函数调用形式的宏定义。

如果断言失败，gtest会返回错误信息和行数。开发人员也可以自己定义显示在后面的文字，增加更丰富的错误信息帮助排查错误。

\paragraph{ASSERT与EXPECT的区别}
gtest里的断言包含2个分类，\verb|ASSERT_*|和\verb|EXPECT_*|开头的，其中\verb|*|代表其他任意字符。

这些宏定义，都定义在头文件\verb|#include <gtest/gtest.h>|中

\begin{itemize}
	\item \verb|ASSERT_*|会生成致命错误，并且立刻终止测试程序。(官方文档中提醒，这里有可能产生内存泄漏，因为这种立即退出可能跳过了后面的回收垃圾的代码，按需可以自行优化)
	\item \verb|EXPECT_*|生成非致命错误，并不会终止测试程序。
\end{itemize}

\subsubsection{增加错误信息}
在\verb|ASSERT_*|和\verb|EXPECT_*|断言之后使用\verb|<<|运算符，后面跟上字符串，可以在报错的时候显示，作为错误信息，辅助开发人员修改错误。
\begin{lstlisting}
ASSERT_EQ(x.size(), y.size()) << "Vectors x and y are of unequal length";

for (int i = 0; i < x.size(); ++i) {
    EXPECT_EQ(x[i], y[i]) << "Vectors x and y differ at index " << i;
}
\end{lstlisting}

\section{创建测试}
\subsubsection{创建测试 - TEST()}
使用宏定义\verb|TEST()|来创建一个测试。这里面可以放任何C++代码，以及gtest的宏定义。

\verb|TEST()|具有类似于C++中函数的形式，但是他没有返回类型。他是由其内部的断言来决定他返回的结果的。如果测试内部发生了错误(包括致命错误和非致命错误)，或者测试内部直接崩溃了，那么他就返回为失败。否则的话，他返回为成功。
\begin{lstlisting}
TEST(TestSuiteName, TestName) {
    ... test body ...
}
\end{lstlisting}

第一个参数\verb|TestSuiteName|是测试套件的名称，第二个参数\verb|TestName|是测试的名，他们都必须是C++合法的标识符，但是，不能有下划线。

不同测试套件里的测试，可以具有相同的名称。(这一点有点类似命名空间)

\subsubsection{示例}
对于下面这样的一个函数
\begin{lstlisting}
int Factorial(int n);  // Returns the factorial of n
\end{lstlisting}

他的gtest测试代码，可以这样写。其中\verb|EXPECT_EQ()|的功能是判断传入的两个值是否相等。
\begin{lstlisting}
// Tests factorial of 0.
TEST(FactorialTest, HandlesZeroInput) {
    EXPECT_EQ(Factorial(0), 1);
}

// Tests factorial of positive numbers.
TEST(FactorialTest, HandlesPositiveInput) {
    EXPECT_EQ(Factorial(1), 1);
    EXPECT_EQ(Factorial(2), 2);
    EXPECT_EQ(Factorial(3), 6);
    EXPECT_EQ(Factorial(8), 40320);
}
\end{lstlisting}

\section{测试夹具}
参考资料: Google Test 官方文档 GoogleTest Primer 页面 \href{https://google.github.io/googletest/primer.html}{Test Fixtures: Using the Same Data Configuration for Multiple Tests} 一节。
\subsection{测试夹具}
如果发现，有一些测试往往采用的是相同的数据，则可以使用测试夹具，他支持在不同的测试之间复用一些共同的内容。

\subsection{使用测试夹具的方法}
\begin{enumerate}
    \item 编写一个类，继承自\verb|testing::Test|。并且使用\verb|protected|来声明他的成员，因为我们不需要在其子类中访问他的成员。
    \item 定义那些可复用的共同内容。
    \item 定义函数\verb|SetUp()|，这函数实际上是重写父类的方法，所以可以使用\verb|override|关键字。
    \item 定义函数\verb|TearDown()|，释放资源。
    \item 还可以按需定义其他方法。
\end{enumerate}

\subsubsection{TEST\_F()}
使用测试夹具时，要使用\verb|TEST_F()|而不是\verb|TEST()|。第一个参数\verb|TestFixtureClassName|是测试夹具的名称。
\begin{lstlisting}
TEST_F(TestFixtureClassName, TestName) {
    ... test body ...
}
\end{lstlisting}

gtest运行到\verb|TEST_F()|的时候，会在运行时创建一个测试夹具，申请一块新的内存，并用\verb|SetUp()|来初始化他，运行测试，测试运行完成后，通过\verb|TearDown()|来释放这块内存资源，然后删除测试夹具。

测试夹具，是在运行时才创建的，并且同时只存在一个，用到新的就要删除旧的。同一个测试套件中的不同测试，他们使用的是不同的测试夹具。

\subsubsection{示例}
假设我们现在要测试一个先进先出的队列的模板类，待测试的代码如下。
\begin{lstlisting}
template <typename E>  // E is the element type.
class Queue {
public:
    Queue();
    void Enqueue(const E& element);
    E* Dequeue();  // Returns NULL if the queue is empty.
    size_t size() const;
    ...
};
\end{lstlisting}

我们这样定义一个测试夹具\verb|QueueTest|
\begin{lstlisting}
class QueueTest : public testing::Test {
protected:
    // 在构造函数中，对q1_和q2_的元素个数进行初始化
    QueueTest() {
        // q0_ remains empty
        q1_.Enqueue(1);
        q2_.Enqueue(2);
        q2_.Enqueue(3);
    }
    
    // ~QueueTest() override = default;
    
    Queue<int> q0_;
    Queue<int> q1_;
    Queue<int> q2_;
};
\end{lstlisting}

写测试代码。
\begin{enumerate}
    \item 使用\verb|q0_|来测试\verb|size()|，看他是否返回0。
    \item 使用\verb|q0_|来测试\verb|Dequeue()|，看他是否返回一个空指针。
    \item 使用\verb|q1_|来测试\verb|Dequeue()|，首先\verb|q1_|是非空的，如果返回空指针那么返回致命错误。对\verb|n|解引用，看他是否是第一个元素1。再看\verb|q1_|是否是空的。
    \item 使用\verb|q2_|来测试\verb|Dequeue()|，同上。
\end{enumerate}

\begin{lstlisting}
TEST_F(QueueTest, IsEmptyInitially) {
    EXPECT_EQ(q0_.size(), 0);
}

TEST_F(QueueTest, DequeueWorks) {
    int* n = q0_.Dequeue();
    EXPECT_EQ(n, nullptr);
    
    n = q1_.Dequeue();
    ASSERT_NE(n, nullptr);
    EXPECT_EQ(*n, 1);
    EXPECT_EQ(q1_.size(), 0);
    delete n;
    
    n = q2_.Dequeue();
    ASSERT_NE(n, nullptr);
    EXPECT_EQ(*n, 2);
    EXPECT_EQ(q2_.size(), 1);
    delete n;
}
\end{lstlisting}

测试夹具的运行过程
\begin{enumerate}
    \item gtest创建一个\verb|QueueTest|对象，假设称为\verb|t1|
    \item 运行第一个测试\verb|IsEmptyInitially|
    \item 析构\verb|t1|
    \item 重复上面的过程，在创建一个对象，运行第二个测试\verb|DequeueWorks|
\end{enumerate}

\subsubsection{RUN\_ALL\_TESTS()}
在通过\verb|TEST()|和\verb|TEST_F()|定义了所有的测试之后，通过\verb|RUN_ALL_TESTS()|运行所有的测试。

\verb|RUN_ALL_TESTS()|这个接口，可以与不同的测试套件甚至不同的待测试代码相连接使用，提供了极大的方便。

运行测试时，会按照\verb|TEST()|和\verb|TEST_F()|声明的顺序来执行测试，不需要重复地调用\verb|TEST|语句。

当调用\verb|RUN_ALL_TESTS()|语句后，会按顺序执行如下步骤
\begin{enumerate}
    \item 保存所有gtest标志位
    \item 为第1个测试创建测试夹具对象。
    \item 通过\verb|SetUp()|来初始化该测试夹具对象。
    \item 基于这测试夹具对象，运行测试。
    \item 通过\verb|TearDown()|来回收资源。
    \item 删除测试夹具对象。
    \item 再次存储所有gtest标志位
    \item 重复上述步骤
\end{enumerate}

如果发生了致命错误，那么会立即退出，后面还有的剩余步骤会被直接跳过。

注意，主函数\verb|main()|必须将\verb|RUN_ALL_TESTS()|的返回值返回，因为测试程序判断测试是否成功是通过返回值，而不是具体什么错误信息，或者是标准输出流上的内容。

而且，\verb|RUN_ALL_TESTS()|只能调用1次。这是官方文档里面的要求，调用多次可能会带来致命错误。

\subsection{编写测试程序的主函数}
用户是不需要自己编写\verb|main()|的，gtest把主函数放在了自己的静态库\verb|gtest_main|中，在构建的时候需要进行链接。

如果自己编写了\verb|main()|，那么主函数一定要返回\verb|RUN_ALL_TESTS()|。

\verb|testing::InitGoogleTest()|，初始化gtest框架，主要是一些标志位。他必须在\verb|RUN_ALL_TESTS()|之前被调用，否则\verb|RUN_ALL_TESTS()|所需要的一些标志位未被正确初始化，会产生错误。

\subsubsection{一个典型的测试程序的主函数}
\begin{lstlisting}
#include "gtest/gtest.h"

int main(int argc, char **argv) {
    ::testing::InitGoogleTest(&argc, argv);
    return RUN_ALL_TESTS();
}
\end{lstlisting}

\section{进阶技巧 - Advanced GoogleTest Topics}
参考资料: Google Test 官方文档 \href{https://google.github.io/googletest/advanced.html}{Advanced Topics} 页面。
\subsection{更多可用的断言 - More Assertions}
\subsubsection{显式地报告成功和错误 - Explicit Success and Failure}
大多用于控制流中，而非布尔表达式中。比如下面的代码示例
\begin{lstlisting}
switch(expression) {
    case 1:
        ... some checks ...
    case 2:
        ... some other checks ...
    default:
        FAIL() << "We shouldn't get here.";
}
\end{lstlisting}

\verb|SUCCESS()|

\verb|FAIL()|，产生一个致命错误。只能用在返回\verb|void|的函数中。

\verb|ADD_FAILURE()|，产生一个非致命错误。

\verb|ADD_FAILURE_AT(file_path,line_number)|，产生一个非致命错误，并且让他报告在某个文件的某一行上。

\subsubsection{关于异常机制的断言 - Exception Assertions}
以下断言用于验证某些代码，是否抛出了异常。

使用这些断言，首先需要当前环境支持异常机制。

\verb|EXPECT_THROW(statement, exception_type)|
\verb|ASSERT_THROW(statement, exception_type)|

\verb|EXPECT_ANY_THROW(statement)|
\verb|ASSERT_ANY_THROW(statement)|

\verb|EXPECT_NO_THROW(statement)|
\verb|ASSERT_NO_THROW(statement)|

\subsubsection{能提供更友好的错误信息的谓词断言 - Predicate Assertions for Better Error Messages}
由于无法涵盖所有测试情景，所以有些时候，需要用比如\verb|EXPECT_TRUE()|这样简易的断言来测试复杂的逻辑，这个时候输出的测试结果，往往也难以阅读。

gtest提供了3种方法来解决这一问题。

\paragraph{使用现有的布尔函数}
一般把返回\verb|true|或者\verb|false|的函数，称为谓词(或谓词函数)。

gtest提供了\verb|EXPECT_PRED*|系列断言，其中\verb|*|可替换为1到5这几个数字。

验证谓词\verb|pred|在传入给定的几个参数时是否返回\verb|true|，如果返回\verb|true|则断言成功，否，则断言失败。

如果断言失败，会打印出给定参数的每一个值，显示比较详细的一些错误信息。

\begin{lstlisting}
EXPECT_PRED1(pred,val1)
EXPECT_PRED2(pred,val1,val2)
EXPECT_PRED3(pred,val1,val2,val3)
EXPECT_PRED4(pred,val1,val2,val3,val4)
EXPECT_PRED5(pred,val1,val2,val3,val4,val5)

ASSERT_PRED1(pred,val1)
ASSERT_PRED2(pred,val1,val2)
ASSERT_PRED3(pred,val1,val2,val3)
ASSERT_PRED4(pred,val1,val2,val3,val4)
ASSERT_PRED5(pred,val1,val2,val3,val4,val5)
\end{lstlisting}

举例，以下的测试过程。

在测试\verb|EXPECT_PRED2(MutuallyPrime, b, c);|中，控制台则会输出一些错误信息，如下所示。
\begin{lstlisting}
// 如果m和n没有公因子时，则返回true
bool MutuallyPrime(int m, int n) { ... }
...
const int a = 3;
const int b = 4;
const int c = 10;
...
EXPECT_PRED2(MutuallyPrime, a, b);  // Succeeds
EXPECT_PRED2(MutuallyPrime, b, c);  // Fails

// output
// MutuallyPrime(b, c) is false, where
// b is 4
// c is 10
\end{lstlisting}

如果待测试的函数，是模板函数，那么需要显式地指定他的类型。如果只写\verb|EXPECT_PRED1(IsPositive, 5);|的话，会报错。
\begin{lstlisting}
EXPECT_PRED1(static_cast<bool (*)(int)>(IsPositive), 5);
EXPECT_PRED1(static_cast<bool (*)(double)>(IsPositive), 3.14);
\end{lstlisting}

\begin{lstlisting}
template <typename T>
bool IsNegative(T x) {
    return x < 0;
}
...
EXPECT_PRED1(IsNegative<int>, -5);  // Must specify type for IsNegative
\end{lstlisting}

\begin{lstlisting}
ASSERT_PRED2((MyPredicate<int, int>), 5, 0);
\end{lstlisting}

\paragraph{使用能返回"断言返回值"的函数 - Using a Function That Returns an AssertionResult}
gtest提供了\verb|::testing::AssertionResult|类。

\verb|AssertionResult|对象也可以连用\verb|<<|运算符来连接一个流，输出额外的错误信息。

下面的代码示例，展示了\verb|AssertionResult|对象是如何替代\verb|EXPECT_TRUE()|的。
\begin{lstlisting}
testing::AssertionResult IsEven(int n) {
    if ((n % 2) == 0)
        return testing::AssertionSuccess();
    else
        return testing::AssertionFailure() << n << " is odd";
}

// instead of
// bool IsEven(int n) {
//     return (n % 2) == 0;
// }
\end{lstlisting}

\paragraph{使用谓词格式器 - Using a Predicate-Formatter}
谓词格式器断言(predicate-formatter assertions)

\verb|EXPECT_PRED_FORMAT*|一系列断言

\subsubsection{比较浮点数 - Floating-Point Comparison}
比较浮点数的操作，可能不是很常用。我们提供谓词形式的函数(predicate-format functions)，以便于可以用于谓词断言宏\verb|EXPECT_PRED_FORMAT2|当中。

下面的代码示例，检测的是变量\verb|val1|是否小于，或者近似等于变量\verb|val2|。
\begin{lstlisting}
using ::testing::FloatLE;
using ::testing::DoubleLE;
...
EXPECT_PRED_FORMAT2(FloatLE, val1, val2);
EXPECT_PRED_FORMAT2(DoubleLE, val1, val2);
\end{lstlisting}

\subsubsection{更多字符串断言 - More String Assertions}
gmock提供了字符串匹配器(string matchers)与\verb|EXPECT_THAT|宏一起使用。检查是否包含某个子字符串，按正则表达式匹配，等功能。
\begin{lstlisting}
using ::testing::HasSubstr;
using ::testing::MatchesRegex;
...
ASSERT_THAT(foo_string, HasSubstr("needle"));
EXPECT_THAT(bar_string, MatchesRegex("\\w*\\d+"));
\end{lstlisting}

\subsubsection{类型断言 - Type Assertions}
下面的函数，可以用来检测类型\verb|T1|和\verb|T2|是否是同一个类型。断言成立时，函数什么都不会做。而断言一旦失败，则会编译失败，并且编译器会给出信息即\verb|T1|和\verb|T2|并不是同一个类型。

这个技巧模板元编程当中非常实用。
\begin{lstlisting}
::testing::StaticAssertTypeEq<T1, T2>();
\end{lstlisting}

如果这个函数，在一个类模板的成员函数模板内部使用，或者在函数模板内部使用，则成员函数模板或函数模板必须要实例化才行。比如我们有如下的代码
\begin{lstlisting}
template <typename T> class Foo {
public:
	void Bar() { testing::StaticAssertTypeEq<int, T>(); }
};
\end{lstlisting}

下面的测试代码，不会产生编译错误。因为\verb|Foo<bool>::Bar()|并没有实例化。没有调用的函数模板是不会被实例化的。
\begin{lstlisting}
void Test1() { Foo<bool> foo; }
\end{lstlisting}

写成下面的形式，则会产生编译错误，说明\verb|testing::StaticAssertTypeEq<int, T>()|起作用了。
\begin{lstlisting}
void Test2() { Foo<bool> foo; foo.Bar(); }
\end{lstlisting}

\subsection{跳过测试 - Skipping test execution}
根据\verb|SUCCEED()|和\verb|FAIL()|断言，可以使用\verb|GTEST_SKIP()|宏定义，在运行时控制不再执行后续的测试。这个功能，在测试系统时比较有用，也就是说，不需要真的执行完所有测试流程，跳过他们，只进行一些系统上、框架上的测试。

宏定义\verb|GTEST_SKIP()|可以在单独的测试任务里使用，或者在\verb|SetUp()|成员方法里使用。

宏定义\verb|GTEST_SKIP()|后面也可以使用重载了的左移运算符来附加一些信息。

\paragraph{代码示例}
在下面的代码示例中，\verb|SkipTest|和\verb|SkipFixture|都不会被执行
\begin{lstlisting}
TEST(SkipTest, DoesSkip) {
    GTEST_SKIP() << "Skipping single test";
    EXPECT_EQ(0, 1);  // Won't fail; it won't be executed
}

class SkipFixture : public ::testing::Test {
    protected:
    void SetUp() override {
        GTEST_SKIP() << "Skipping all tests for this fixture";
    }
};

// Tests for SkipFixture won't be executed.
TEST_F(SkipFixture, SkipsOneTest) {
    EXPECT_EQ(5, 7);  // Won't fail
}
\end{lstlisting}

\subsection{控制输出的格式 - Teaching GoogleTest How to Print Your Values}
当某个\verb|EXPECT_*|断言失败的时候，gtest会返回一些提示信息，帮助开发者排除故障。

支持一些C++内置的类型，也支持\verb|<<|符号。

\subsection{防止致命后果的测试 - Death Tests}
有些特殊的断言，他们一旦失败会导致严重后果，比如内存崩溃等。他们的存在，目的就是在程序发生致命错误之后迅速的报告失败，然后中断测试，以免更严重的后果发生。我们将这种具有特殊重要性的断言，称为 death tests 。

\subsubsection{如何编写一个death test}
对于下面的代码示例。
\begin{lstlisting}
TEST(MyDeathTest, Foo) {
	// This death test uses a compound statement.
	ASSERT_DEATH({
		int n = 5;
		Foo(&n);
	}, "Error on line .* of Foo()");
}

TEST(MyDeathTest, NormalExit) {
	EXPECT_EXIT(NormalExit(), testing::ExitedWithCode(0), "Success");
}

TEST(MyDeathTest, KillProcess) {
	EXPECT_EXIT(KillProcess(), testing::KilledBySignal(SIGKILL),
	"Sending myself unblockable signal");
}
\end{lstlisting}

上面的代码示例，主要做了以下几件事
\begin{enumerate}
	\item 调用\verb|Foo(5)|，使得进程中止，并且输出给出的错误信息
	\item 调用\verb|NormalExit()|，进程向标准错误流输出\verb|Success|，退出码0
	\item 调用\verb|KillProcess()|，使用信号\verb|SIGKILL|来中止进程
\end{enumerate}

death test主要关注一下几件事。
\begin{enumerate}
	\item 传入的语句是否中止了该进程，或者使得进程停止。
	\item 在\verb|ASSERT_EXIT|和\verb|EXPECT_EXIT|情况下，退出的情况是否符合谓词的判定。或者在\verb|ASSERT_DEATH|和\verb|EXPECT_DEATH|情况下，进程是否以非0的退出码退出。
	\item 标准错误流输出的字符串是否匹配。
\end{enumerate}

\subsection{Using Assertions in Sub-routines}
如果同时执行的测试太多，最终输出的测试结果信息，如果其中有失败的测试，可能会分不清这部分失败的测试来自于那些测试用例。在这种情况下，可以使用宏定义\verb|SCOPED_TRACE|，或者\verb|ScopedTrace|。
\begin{lstlisting}
SCOPED_TRACE(message);
ScopedTrace trace("file_path", line_number, message);
\end{lstlisting}

其中\verb|message|可以是任何可被传输至流\verb|std::ostream|当中的数据。

宏定义\verb|SCOPED_TRACE|会将当前文件名，行号以及一些信息，在发生测试错误时输出出来，以便于定位。

对于下面的代码示例
\begin{lstlisting}
10: void Sub1(int n) {
11:   EXPECT_EQ(Bar(n), 1);
12:   EXPECT_EQ(Bar(n + 1), 2);
13: }
14:
15: TEST(FooTest, Bar) {
16:   {
17:     SCOPED_TRACE("A");  // This trace point will be included in
18:                         // every failure in this scope.
19:     Sub1(1);
20:   }
21:   // Now it won't.
22:   Sub1(9);
23: }
\end{lstlisting}

会得到下面类似的信息
\begin{lstlisting}
path/to/foo_test.cc:11: Failure
Value of: Bar(n)
Expected: 1
Actual: 2
Google Test trace:
path/to/foo_test.cc:17: A

path/to/foo_test.cc:12: Failure
Value of: Bar(n + 1)
Expected: 2
Actual: 3
\end{lstlisting}

如果没有一些额外的信息，就很难知道是哪一个\verb|Sub1()|报告的错误。

\subsection{Logging Additional Information}

\subsection{在测试套件之中共享数据 - Sharing Resources Between Tests in the Same Test Suite}
\begin{enumerate}
    \item 在测试夹具中，声明一些\verb|static|静态成员，存放这些共享数据。
    \item 在测试夹具的外部，定义那些需要赋初值的内容。
    \item 在这个测试夹具内，定义一个公有方法\verb|static void SetUpTestSuite()|用来配置公有变量，定义一个公有方法\verb|static void TearDownTestSuite()|用来回收资源。
\end{enumerate}

gtest在运行这个测试套件的第1个测试之前，先会调用\verb|SetUpTestSuite()|，在运行完最后一个测试之后，会调用\verb|TearDownTestSuite()|，也就是在删除最后一个测试夹具对象之后。在这个时间范围内，是一直可以使用公用资源的。

\verb|SetUpTestSuite()|可能调用不止一次。

派生类，也是可以使用公有变量的，所以要注意内存管理，并且防止内存泄露。

\subsection{Global Set-Up and Tear-Down}
可以在测试级别和测试套件级别使用\verb|setup|和\verb|teardown|，那么同样地也可以在全局级别使用\verb|SetUp()|和\verb|TearDown()|。
\begin{enumerate}
	\item 继承自\verb|::testing::Environment|
\begin{lstlisting}
class Environment : public ::testing::Environment {
	public:
	~Environment() override {}
	
	// Override this to define how to set up the environment.
	void SetUp() override {}
	
	// Override this to define how to tear down the environment.
	void TearDown() override {}
};
\end{lstlisting}
	\item 通过函数\verb|::testing::AddGlobalTestEnvironment()|实例化一个对象
\begin{lstlisting}
Environment* AddGlobalTestEnvironment(Environment* env);
\end{lstlisting}	
\end{enumerate}

首先有以下前提，环境配置运行时并没有任何报错，而且也没有调用\verb|GTEST_SKIP()|，那么，在运行\verb|RUN_ALL_TESTS()|的时候，他首先会调用\verb|SetUp()|。

是可以有多个环境对象同时存在的，他们的 \verb|SetUp()| 方法按照注册顺序被调用，而 \verb|TearDown()| 方法则按相反顺序被调用。

\subsection{参数化测试 - Value-Parameterized Tests}
减少使用相同数据进行测试时，重复代码的拷贝次数，增加测试代码的可复用性。

\subsubsection{How to Write Value-Parameterized Tests}
定义一个测试夹具，并继承自\verb|testing::Test|和\verb|testing::WithParamInterface<T>|。

这里的类型参数\verb|T|是测试所使用的数据的类型。\verb|T|必须可拷贝。如果\verb|T|是传统指针，则必须注意生命周期管理。

为了方便，也可以写作只继承自\verb|testing::TestWithParam<T>|。

测试夹具如果定义了\verb|SetUpTestSuite()|和\verb|TearDownTestSuite()|，则必须将他们声明为\verb|public|而非\verb|protected|，这样才能使用\verb|TEST_P|。

\begin{lstlisting}
class FooTest :
public testing::TestWithParam<absl::string_view> {
    // 可以在这里实现所有常用的测试夹具类(fixture class)
    // 访问测试参数，可以通过TestWithParam<T>类的GetParam()方法
};

// 或者，通过新的派生类，来给已有的夹具类增加参数
class BaseTest : public testing::Test {
    ...
};
class BarTest : public BaseTest,
public testing::WithParamInterface<absl::string_view> {
    ...
};
\end{lstlisting}

使用宏定义\verb|TEST_P|和上述的测试夹具，来定义测试。这里，后缀\verb|_P|的意思就是parameterized或者pattern。
\begin{lstlisting}
TEST_P(FooTest, DoesBlah) {
    // Inside a test, access the test parameter with the GetParam() method
    // of the TestWithParam<T> class:
    EXPECT_TRUE(foo.Blah(GetParam()));
    ...
}

TEST_P(FooTest, HasBlahBlah) {
    ...
}
\end{lstlisting}

使用宏定义\verb|INSTANTIATE_TEST_SUITE_P|来初始化测试套件(test suite)。

\verb|INSTANTIATE_TEST_SUITE_P|的第一个参数是测试套件实例的唯一名称，第二个参数是测试的名称，第三个参数是参数生成器(parameter generator)。

比如下面的代码示例中，从测试套件\verb|FooTest|进行实例化，实例名为\verb|MeenyMinyMoe|，使用的参数值，是由参数生成器\verb|Values|通过\verb|"meeny", "miny", "moe"|生成出来的。
\begin{lstlisting}
INSTANTIATE_TEST_SUITE_P(MeenyMinyMoe,
                            FooTest,
                            testing::Values("meeny", "miny", "moe"));
\end{lstlisting}

\subsection{Typed Tests}

\subsection{Type-Parameterized Tests}

\subsection{Testing Private Code}

\subsection{“Catching” Failures}

\subsection{Registering tests programmatically}

\subsection{Getting the Current Test’s Name}

\subsection{Extending GoogleTest by Handling Test Events}

\subsection{Running Test Programs: Advanced Options}

\section{gmock}
\subsection{简介}
一个mock对象，他从接口层面看上去，表现得像真实的对象一样。也就是，他完美地模拟了所需要的对象的接口，而并不严格模拟内部的逻辑，只模拟接口。

\subsubsection{如何使用mock对象}
\begin{enumerate}
    \item 需要使用宏，来描述你想mock什么样的接口。
    \item 创建一些mock对象，并直观地指定其预期的行为
    \item 使用mock对象一起进行测试，
\end{enumerate}

\chapter{valgrind - 调试测试工具集合}
\section{valgrind简介}
Valgrind 由内核(core)以及基于内核的其他调试工具组成。内核类似于一个框架(framework)，它模拟了一个CPU环境，并提供服务给其他工具；而其他工具则类似于插件 (plug-in)，利用内核提供的服务完成各种特定的内存调试任务。

Valgrind 是免费开源软件，符合GNU的GNU General Public License, version 2协议。

\subsection{组成}
Valgrind 由许多工具集合而成，其中主要包括以下内容：
\begin{enumerate}
    \item memcheck，这是valgrind应用最广泛的工具，一个重量级的内存检查器，能够发现开发中绝大多数内存错误使用情况，比如：使用未初始化的内存，使用已经释放了的内存，内存访问越界等。
    \item callgrind，主要用来检查程序中函数调用过程中出现的问题。
    \item cachegrind，主要用来检查程序中缓存使用出现的问题。
    \item helgrind，主要用来检查多线程程序中出现的竞争问题。
    \item massif，主要用来检查程序中堆栈使用中出现的问题。
    \item extension，可以利用core提供的功能，自己编写特定的内存调试工具。
\end{enumerate}

\subsection{memcheck - 内存管理检测工具}
\paragraph{特点}
当 memcheck 开始监视某个程序的运行时，将会记录任何对内存的读和写操作，检测所有的\verb|malloc|，\verb|free|，\verb|new|，\verb|delete|操作。

memcheck 工具会在发现问题的时候报告其源代码的位置，也能够记录经过何种调用栈引发了这个问题。

memcheck 工具，在字节级别检测内存地址的读写，以bit级别检测变量的初始化过程，正因如此，它能够检测出错误使用了哪怕1个未正确初始化的bit。

memcheck 工具在检测程序运行时，程序运行的速度可能比正常情况下慢10倍甚至30倍。

\paragraph{针对的问题}
memcheck 工具主要面向以下问题
\begin{enumerate}
    \item 访问不应访问的内存。
    \item 以危险方式使用了未初始化的变量。
    \item 内存泄露。
    \item 错误地释放堆内存，比如重复释放，或者释放了不属于他的堆内存。
    \item 在使用\verb|memcpy()|及相关函数时，源内存块与目标内存块有重叠(overlapping)，这种使用方式会导致未定义行为UB。
\end{enumerate}

\subsection{Cachegrind - 缓存分析}
Cachegrind 是一个缓存分析器，它可以对CPU中的I1、D1和L2缓存进行详细的模拟，因此可以准确地找出代码中缓存未命中的来源。它可以识别每行源代码的缓存未命中数、内存引用数和执行指令数，并提供每个函数、每个模块和整个程序的摘要。它适用于任何语言编写的程序。Cachegrind 运行程序的速度比普通程序慢20-100倍。

\subsubsection{Callgrind - Cachegrind的扩展}
Callgrind是Josef Weidendorfer开发的一个Cachegrind扩展，它提供了Cachegrind的所有信息，以及关于调用图的额外信息。在3.2.0版本中，它被合并到主要的Valgrind发行版中。另外，还有一个很棒的可视化工具KCachegrind，它可以更好地展示Callgrind收集的数据；也可以用于可视化Cachegrind的输出。

\subsection{Massif - 堆分析器}
Massif是一个堆分析器，它通过定期获取程序的堆快照来执行详细的堆分析。它生成一个显示堆使用情况随时间变化的图表，包括有关哪些程序部分负责最多内存分配的信息。图表由一个文本或HTML文件补充，其中包含更多信息以确定在哪里分配了最多的内存。

\subsection{Helgrind - 线程分析器}
Helgrind是一个线程调试器，用于在多线程程序中查找数据竞争。它寻找被多个(\verb|POSIX p-|)线程访问的内存位置，但找不到一致使用的\verb|pthread_mutex_|锁。这些位置表明线程之间缺少同步，可能导致难以发现的时间依赖性问题。对于使用\verb|pthreads|的任何程序都非常有用。

目前仍然处于试验阶段。

\subsubsection{DRD}
DRD是一个用于检测多线程C和C++程序中错误的工具。该工具适用于使用POSIX线程原语或在POSIX线程原语之上构建的线程概念的任何程序。虽然Helgrind可以检测锁定顺序违规，但对于大多数程序，DRD在进行分析时需要较少的内存。

\section{Quick Start}
\subsubsection{传入要检测的程序}
假设正常运行一个程序，有如下的命令
\begin{lstlisting}
myprog arg1 arg2
\end{lstlisting}

那么，使用 valgrind 工具来检测程序 myprog ，就要以如下的方式。
\begin{lstlisting}
valgrind --leak-check=yes myprog arg1 arg2
\end{lstlisting}

\subsubsection{使用示例}
假如有如下源代码，其中包含2个错误，1个内存引用错误，1个内存泄露。
\begin{lstlisting}
#include <stdlib.h>

void f(void) 
{
    int* x = malloc(10 * sizeof(int));
    x[10] = 0;        // problem 1: heap block overrun
}                    // problem 2: memory leak -- x not freed

int main(void) 
{
    f();
    return 0;
}
\end{lstlisting}

\paragraph{内存引用错误}
将上述源代码编译后，并使用 valgrind 工具进行检测，对于内存引用错误(problem 1)，可能会得到如下的输出报告。
\begin{lstlisting}
==19182== Invalid write of size 4
==19182==    at 0x804838F: f (example.c:6)
==19182==    by 0x80483AB: main (example.c:11)
==19182==  Address 0x1BA45050 is 0 bytes after a block of size 40 alloc' d
==19182==    at 0x1B8FF5CD: malloc (vg_replace_malloc.c:130)
==19182==    by 0x8048385: f (example.c:5)
==19182==    by 0x80483AB: main (example.c:11)
\end{lstlisting}

\begin{itemize}
    \item \verb|19182|是进程ID，这个因进程而已，起到定位和标识的作用。
    \item 第一行中\verb|Invalid write...|字样，表明了发生错误的种类。
    \item 在第一行后面，有调用栈，表示了这个错误是如何通过各级调用而发生的。使用\verb|--num-callers|选项，可以增加调用栈的显示信息。
    \item 代码的地址，一般来说不重要，对于分析复杂问题比较重要。
    \item 这里显示的信息表示，被写入的内存，正好处于第5行通过\verb|malloc()|分配的内存块的结尾处。
\end{itemize}

\paragraph{内存泄露}
对于内存泄露错误(problem 2)，可能会得到如下的输出报告。
\begin{lstlisting}
==19182== 40 bytes in 1 blocks are definitely lost in loss record 1 of 1
==19182==    at 0x1B8FF5CD: malloc (vg_replace_malloc.c:130)
==19182==    by 0x8048385: f (a.c:5)
==19182==    by 0x80483AB: main (a.c:11)
\end{lstlisting}

\begin{itemize}
    \item 调用栈展示了在何处发生的内存泄露(第5行)。
    \item 内存泄漏错误，分为2种，\verb|definitely lost|是必须修复的，\verb|probably lost|可能仅在一些特殊情况发生内存泄露，其他一般情况并不会发生内存泄漏。
\end{itemize}

\subsubsection{检测其他错误}
memcheck 还可以检测其他错误，比如使用未初始化的变量，这种情况一般会打印信息\verb|Conditional jump or move depends on uninitialised value(s)|，可以使用选项\verb|--track-origins=yes|以显示更多信息。

更多错误种类及打印的信息，请查阅 valgrind 手册。

\section{Valgrind User Manual}
\subsection{Using and understanding the Valgrind core}
\subsubsection{What Valgrind does with your program}
valgrind 从设计上实现了非入侵性(non-intrusive)，他可以直接检测已有的可执行程序，而不需要为了检测而重新编译。

\paragraph{基本使用格式}
\begin{lstlisting}
valgrind [valgrind-options] your-prog [your-prog-options]
\end{lstlisting}

\paragraph{选项--tool}
选项\verb|--tool|是最重要的一个选项，他告诉 valgrind 你要选用它里面的哪一个工具进行此次检测。默认值是 memcheck 工具。

\paragraph{检测过程}
检测开始前 valgrind 工具已经接管待测的可执行程序了。该程序会运行在一个由 valgrind core 构造的虚拟 CPU 上。

valgrind 首先将程序交给所选的检测工具，检测工具会在其上加上检测的代码，然后合并起来交给 valgrind core 来运行。新增的代码量，因工具种类而已，或多或少会减慢程序的运行速度，使得他比正常运行的速度要慢，这是正常现象。

\paragraph{错误抑制机制与选项--gen-suppressions=yes}
如果选择的是错误检测工具，valgrind 还会检测涉及到的库中的错误，但是可能这些错误并不是当前的开发人员开发的，所以我们对于这些错误并不感兴趣。这就需要编写错误抑制配置文件(suppressions file)，来让 valgrind 不要显示这些文件中的错误。

与错误抑制机制相关的选项是\verb|--gen-suppressions=yes|

\subsubsection{Getting started}
待测程序构建时，要加上\verb|-g|选项，为构建的可执行程序增加可调试信息。

在检测C++程序时，要加上\verb|-fno-inline|选项，可以有效展示出函数的调用栈。当使用这个选项时，编译器不会将任何函数内联展开，除了那些带有 \verb|always_inline| 属性的函数。在不进行优化的情况下，这是默认设置。单个函数可以通过添加 \verb|noinline| 属性来避免内联。

如果启用了内联优化，也可以使用 valgrind 的\verb|--read-inline-info=yes|选项，来更友好地显示函数调用栈。

\subsubsection{The Commentary}
valgrind 工具，会显示一条字符串输出流，用于说明错误相关信息。其基本格式如下
\begin{lstlisting}
==12345== some-message-from-Valgrind
\end{lstlisting}

\paragraph{进程ID}
\verb|12345|位置处，代表的是进程ID。在排查多进程情况时有用。

\paragraph{选项-v}
valgrind 选项\verb|-v|可以显示更多信息

\paragraph{重定向}
可以通过重定向，将 valgrind 的输出流重定向到3个位置。
\begin{enumerate}
    \item 默认。重定向到文件描述符上。默认是\verb|2|，也就是\verb|stderr|。可以通过\verb|--log-fd=N|来配置输出到其他文件描述符，其中\verb|N|表示文件描述符。
\end{enumerate}

\chapter{redis - 内存数据库}
\section{安装}
redis是开源软件，可以从官网以压缩包形式获取源代码。

解压之后，在根目录下直接使用make进行构建。

然后输入\verb|make PREFIX=/usr/local/redis install|，可以将redis的相关库安装到\verb|/usr/local/redis|路径下。

\paragraph{检测是否安装成功}
可以运行redis服务端\verb|redis-server|，和redis客户端\verb|redis-cli|，检测是否安装成功。

\paragraph{启动服务端}
可以指定端口号。

\verb|redis-server [--port 6379]|

如果参数过多，可以通过配置文件来传入参数。

例如\verb|redis-server myconfig/redis.conf|

\paragraph{启动客户端}
\verb|redis-cli [-h 127.0.0.1 -p 6379]|

\paragraph{获取所有的key}
\verb|keys *|

\paragraph{删除所有的redis数据}
\verb|flushall|

\paragraph{服务器统计信息}
\verb|info|，不需要其他参数。

\section{数据类型}
redis支持存储多种数据类型
\begin{enumerate}
    \item \verb|string|:字符串类型，是最基本的数据单元。redis接收到的任何数据，默认以\verb|string|类型存储
    \item \verb|hash|:哈希表，是一个键值对的集合
    \item \verb|list|:列表，连续、有序的数据结构，可以存储一系列\verb|string|元素
    \item \verb|set|:集合，无序的数据结构，存储不重复的\verb|string|元素。如果插入重复的值也不会插入成功。
    \item \verb|zset|:有序集合，但是每个元素都有一个\verb|score|与之相关联。
    \item \verb|bitmap|:位图，可以对每个位进行操作
    \item \verb|HyperLogLogs|:用于基数统计，可以估算集合中的唯一元素数量。
    \item \verb|Geospatial|:用于存储地理位置信息。
    \item \verb|Pub/Sub|:一种消息通信模式，允许客户端订阅消息通道，并接收发布到该通道的消息。
    \item \verb|Streams|:用于消息队列和日志存储，支持消息的持久化和时间排序。
    \item \verb|Modules|:Redis 支持动态加载模块，可以扩展 Redis 的功能。
\end{enumerate}

\subsubsection{string字符串类型}
\verb|string|是 redis 最基本的类型。他是二进制安全的，任何数据都能存储到 redis 的\verb|string|类型当中。

\begin{table}[!ht]
    \centering
    \caption{string类型的常用命令} \label{redis数据类型string常用命令}
    \begin{tabular}{|p{15em}|p{30em}|}
        \hline
        命令 & 功能 \\ \hline
        \verb|SET key value| & 设置键值对 \\ \hline
        \verb|GET key| & 获取键的值 \\ \hline
        \verb|INCR key| & 将键的值加1 \\ \hline
        \verb|DECR key| & 将键的值减1 \\ \hline
        \verb|APPEND key value| & 将值追加到键的值之后，相当于拼接字符串。 \\ \hline
    \end{tabular}
\end{table}

\subsubsection{hash类型}
\verb|hash|是一个\verb|field|字段和\verb|value|字段的映射表，这二者也都是\verb|string|类型。
\begin{table}[!ht]
    \centering
    \caption{hash类型的常用命令} \label{redis数据类型hash常用命令}
    \begin{tabular}{|p{15em}|p{30em}|}
        \hline
        命令 & 功能 \\ \hline
        \verb|HSET key field value| & 设置哈希表中字段的值。注意，这里的键值对指的是\verb|field-value|，而参数\verb|key|代表的是\verb|hash|。这个\verb|hash|可以存储大量\verb|field-value|对。 \\ \hline
        \verb|HGET key field| & 获取哈希表中指定字段\verb|field|的值。 \\ \hline
        \verb|HGETALL key| & 获取哈希表中所有字段和值。 \\ \hline
        \verb|HDEL key field1 [field2]| & 删除哈希表中的一个或多个字段。 \\ \hline
        \verb|HEXISTS key field| & 查看哈希表 \verb|key| 中，指定的字段是否存在。 \\ \hline
        \verb|HKEYS key| & 获取哈希表中的所有\verb|field|字段。 \\ \hline
        \verb|HLEN key| & 获取哈希表中\verb|field-value|对的数量。 \\ \hline
    \end{tabular}
\end{table}

\subsubsection{list类型}
\verb|list|类型，可以在头部和尾部插入元素。
\paragraph{方向}
在 redis 内置配置中，列表左侧是头部，右侧是尾部。所以在头部插入的命令带有\verb|L|前缀，在尾部插入的命令带有\verb|R|前缀。
\paragraph{容量}
1个\verb|list|最多可以包含$2^(32)-1$个元素。
\begin{table}[!ht]
    \centering
    \caption{list类型的常用命令} \label{redis数据类型list常用命令}
    \begin{tabular}{|p{20em}|p{25em}|}
        \hline
        命令 & 功能 \\ \hline
        \verb|LPUSH key value| & 将值插入到列表头部。 \\ \hline
        \verb|RPUSH key value| & 将值插入到列表尾部。 \\ \hline
        \verb|LPOP key| & 移出并获取列表的第一个元素。 \\ \hline
        \verb|RPOP key| & 移出并获取列表的最后一个元素。 \\ \hline
        \verb|LRANGE key start stop| & 获取列表在指定范围内的元素。 \\ \hline
        \verb|LINDEX key index| & 通过索引获取列表中的元素 \\ \hline
        \verb|LLEN key| & 获取列表长度 \\ \hline
        \texttt{LINSERT key BEFORE|AFTER pivot value} & 在列表的元素\verb|pivot|前或者后插入元素，注意，这里的\verb|pivot|是元素的值，而不是index \\ \hline
    \end{tabular}
\end{table}

\subsubsection{set类型}
\verb|set|类型是无序的集合，里面的元素是唯一的，不能重复。

\verb|set|是由哈希表实现的，所以增删查的复杂度都是O(1)

\paragraph{容量}
1个\verb|set|最多可以包含$2^(32)-1$个元素。
\begin{table}[!ht]
    \centering
    \caption{set类型的常用命令} \label{redis数据类型set常用命令}
    \begin{tabular}{|p{20em}|p{25em}|}
        \hline
        命令 & 功能 \\ \hline
        \verb|SADD key value| & 向集合添加一个或多个成员。 \\ \hline
        \verb|SREM key value| & 移除集合中的一个或多个成员。 \\ \hline
        \verb|SMEMBERS key| & 返回集合中的所有成员。 \\ \hline
        \verb|SISMEMBER key value| & 判断值是否是集合的成员。 \\ \hline
    \end{tabular}
\end{table}

\subsubsection{zset类型}
\verb|zset|类型，与\verb|set|类型的相同之处是，他也是无序集合且不允许有重复元素。

不同之处是，\verb|zset|中每个元素都带有一个\verb|double|类型的数值，redis 通过这个数值来对元素从小到大进行排列。
\begin{table}[!ht]
    \centering
    \caption{zset类型的常用命令} \label{redis数据类型zset常用命令}
    \begin{tabular}{|p{20em}|p{25em}|}
        \hline
        命令 & 功能 \\ \hline
        \verb|ZADD key score value| & 向有序集合添加一个或多个成员，或更新已存在成员的分数。 \\ \hline
        \verb|ZRANGE key start stop [WITHSCORES]| & 返回指定范围内的成员。 \\ \hline
        \verb|ZREM key value| & 移除有序集合中的一个或多个成员。 \\ \hline
        \verb|ZSCORE key value| & 返回有序集合中，成员的分数值。 \\ \hline
    \end{tabular}
\end{table}

\section{redis配置文件}
redis 的配置文件一般就在他的安装目录下(比如\verb|/usr/local/redis-6.0.0|)，名称为\verb|redis.conf|

\paragraph{配置文件格式}
配置文件本身是一个纯文本文件。

以井号\verb|#|开头的行是注释行。

配置项的名称与配置的内容中间用空格分隔，例如\verb|daemonize no|，表示不以守护进程的方式启动。

\paragraph{相关命令}
在 redis 客户端里，可以使用\verb|config|命令来查看各项配置。

\verb|config get *|，查看所有配置。

\verb|config get name|，查看名为\verb|name|的配置。

\verb|CONFIG SET CONFIG_SETTING_NAME NEW_CONFIG_VALUE|，将名为\verb|CONFIG_SETTING_NAME|的配置，设置为\verb|NEW_CONFIG_VALUE|。

\paragraph{各个配置项的作用}
\begin{table}[!ht]
    \centering
    \caption{redis.conf中各个配置项的作用} \label{redis.conf中各个配置项的作用}
    \begin{tabular}{|p{12em}|p{33em}|}
        \hline
        命令 & 功能 \\ \hline
        \verb|daemonize no| & 是否以守护进程的方式启动 redis 。默认值为\verb|no|，可以修改为\verb|yes| \\ \hline
        \verb|port 6379| & 默认的监听端口 \\ \hline
        \verb|bind 127.0.0.1| & 绑定的主机地址\\ \hline
        \verb|timeout 300| & 当客户端闲置多长秒后关闭连接，如果指定为 0 ，表示关闭该功能 \\ \hline
        \verb|dbfilename dump.rdb| & 指定本地数据库文件名，默认值为 \verb|dump.rdb| \\ \hline
        \verb|dir ./| & 指定本地数据库存放目录 \\ \hline
    \end{tabular}
\end{table}

\section{发布订阅机制}
redis 的发布订阅机制，是一种消息通信模式。发送者(pub)发布消息，订阅者(sub)接收消息。

\paragraph{频道}
发送者(pub)与订阅者(sub)之间的有一个概念称为频道，发布者把消息发布到指定频道上，订阅者从订阅了的频道接收信息。

redis 客户端可以订阅任意数量的频道。

发布者和订阅者之间是完全解耦的，它们之间不直接通信，而是通过Redis服务器来中转消息。

\paragraph{使用方法}
执行发布订阅机制的，是两个 redis 客户端，一方进行订阅，一方进行发布。

客户端在执行订阅命令之后进入了订阅状态， 只能接收 \verb|subscribe|、\verb|psubscribe|、 \verb|unsubscribe|、 \verb|punsubscribe| 的四个命令。

\paragraph{顺序}
消息发布，必须在有客户端订阅之后，才能正确发送。否则会返回0。

新开启的订阅客户端， 无法收到该频道之前的消息， 因为Redis不会对发布的消息进行持久化。
\begin{lstlisting}
127.0.0.1:6379> PUBLISH mychannel2 "123"
(integer) 0
// 在另外一个客户端使用了SUBSCRIBE命令
127.0.0.1:6379> PUBLISH mychannel2 "123"
(integer) 1
\end{lstlisting}

\paragraph{相关命令}
\begin{table}[!ht]
    \centering
    \caption{redis发布订阅机制相关命令} \label{redis发布订阅机制相关命令}
    \begin{tabular}{|p{15em}|p{30em}|}
        \hline
        命令 & 功能 \\ \hline
        \verb|SUBSCRIBE channelName ...| & 订阅名为\verb|channelName|的频道。可以订阅多个，在后面以空格分隔。 \\ \hline
        \verb|PUBLISH channelName message| & 向\verb|channelName|频道发布消息(如果有空格可以使用双引号)。 \\ \hline
        \verb|pubsub channels [pattern]| & 查询活跃的频道 \\ \hline
    \end{tabular}
\end{table}

\section{事务}
redis 的事务机制，实际上就是指令组成的队列，类似于shell脚本或者批处理程序。

\paragraph{特点}
\begin{enumerate}
    \item 批量操作在发送 \verb|EXEC| 命令前被放入队列缓存。
    \item 收到 \verb|EXEC| 命令后进入事务执行，事务中任意命令执行失败，其余的命令依然被执行。
    \item 在事务执行过程，其他客户端提交的命令请求不会插入到事务执行命令序列中。
\end{enumerate}

\paragraph{使用}
输入 \verb|MULTI| 命令，开始一项事务。然后输入的所有指令，都会被按顺序插入到队列中，每次输入一个队列，都会返回一个 \verb|QUEUED| 字样。

最后输入\verb|EXEC|命令，开始执行事务，也就是按顺序执行之前存储进去的指令。

\paragraph{原子性、线程安全}
单个 Redis 命令的执行是原子性的，但 Redis 没有在事务上增加任何维持原子性的机制，所以 Redis 事务的执行并不是原子性的。

\paragraph{指令失败不会中断事务}
redis 的事务不会因为其中的某一条指令执行失败而中断，既不会回滚，也不会放弃余下的指令。而是一直执行下去。

\paragraph{相关命令}
\begin{table}[!ht]
    \centering
    \caption{redis事务机制相关命令} \label{redis事务机制相关命令}
    \begin{tabular}{|p{15em}|p{30em}|}
        \hline
        命令 & 功能 \\ \hline
        \verb|DISCARD| & 取消事务，放弃执行事务块内的所有命令。 \\ \hline
        \verb|EXEC| & 执行所有事务块内的命令。 \\ \hline
        \verb|MULTI| & 标记一个事务块的开始。 \\ \hline
        \verb|UNWATCH| & 取消 WATCH 命令对所有 key 的监视。 \\ \hline
        \verb|WATCH key [key ...]| & 监视一个(或多个) key ，如果在事务执行之前这个(或这些) key 被其他命令所改动，那么事务将被打断。 \\ \hline
    \end{tabular}
\end{table}

\section{流机制stream}
Redis Stream 是 Redis 5.0 版本新增加的数据结构。

Redis Stream 主要用于消息队列(MQ，Message Queue)，Redis 本身是有一个 Redis 发布订阅 (pub/sub) 来实现消息队列的功能，但它有个缺点就是消息无法持久化，如果出现网络断开、Redis 宕机等，消息就会被丢弃。

Redis Stream 提供了消息的持久化和主备复制功能，可以让任何客户端访问任何时刻的数据，并且能记住每一个客户端的访问位置，还能保证消息不丢失。

\chapter{SQLite - 数据库}
\subsubsection{sqlite3\_step}
\begin{lstlisting}
int sqlite3_step(sqlite3_stmt*);    
\end{lstlisting}

SQL语句准备好了之后，指的是\verb|sqlite3_stmt|对象被成功地构建了之后，就需要调用\verb|sqlite3_step|接口，调用1次或者多次，来执行这个SQL语句。

这个接口的返回值是整型类型，返回值一般是以下的一些宏定义 \verb|SQLITE_BUSY|, \verb|SQLITE_DONE|, \verb|SQLITE_ROW|, \verb|SQLITE_ERROR|, or \verb|SQLITE_MISUSE|。

如果\verb|sqlite3_prepare|接口，带有\verb|v2|标志(比如\verb|sqlite3_prepare_v2()|)，则返回值可能还是其他值。

\verb|SQLITE_BUSY|的意思是，数据库引擎并未能获取给出的SQL语句所需要的操作锁。这种情况一般发生在，数据库文件被其他进程占用，或者有多个写操作尝试同时修改数据库时。需要解决数据竞争问题。

\verb|SQLITE_DONE|的意思是，语句已经顺利完成了。

\verb|SQLITE_ROW|的意思是，语句返回了不止1行数据，返回了多行数据，还需要多次调用\verb|sqlite3_step|接口，每次调用都会返回1行数据，直至所有的所获得的数据都处理完成，则返回\verb|SQLITE_DONE|。

\subsubsection{sqlite与多线程场景}
sqlite官网上的一篇文章\href{https://www.sqlite.org/threadsafe.html}{Using SQLite In Multi-Threaded Applications}。

\paragraph{线程工作模式}
sqlite支持三种线程工作模式
\begin{enumerate}
\item 单线程模式。互斥量是禁用的，sqlite在多个线程同时使用时有隐患。
\item 多线程模式。在这种模式下，sqlite是多线程安全的，前提条件是，数据库连接(database connection)和prepared statement，都没有在多个线程下有共用的情况。
\item 串行模式。在串行模式下，API调用，使得使用数据库连接、或者其他基于数据库连接的对象，都变得线程安全了，而不像多线程模式。(多线程下API调用)对于一个数据库对象的影响，就像他们都来自于单线程时一样。串行模式保证了这种顺序和互斥性。
\end{enumerate}

线程模式，可以在编译时、开始使用数据库功能时、运行时进行选择。运行时的配置会覆盖掉之前的配置，数据库初始化时的配置又会覆盖掉编译时的配置。唯一例外是，单线程模式一旦选择了就无法被覆盖。

默认模式为串行模式。

\paragraph{SQLITE\_THREADSAFE}
编译时参数\verb|SQLITE_THREADSAFE|可以选择线程工作模式。

在编译时，通过\verb|-DSQLITE_THREADSAFE=0|(或者\verb|1|和\verb|2|)这样的参数，可以设置。

\verb|sqlite3_threadsafe()|可以返回编译时\verb|SQLITE_THREADSAFE|的数值。
\begin{itemize}
    \item \verb|0|代表单线程模式。
    \item \verb|1|代表串行模式。
    \item \verb|2|代表多线程模式。
\end{itemize}

\chapter{QT - 界面开发}
\section{基本内容}
\subsection{Q\_OBJECT}
\paragraph{Q\_OBJECT}
\verb|Q_OBJECT| 是一个宏，它在 Qt 框架中具有特殊的意义。当你在一个类中使用 \verb|Q_OBJECT| 宏时，它告诉 Qt 的元对象编译器(Meta-Object Compiler，简称 moc)需要为这个类生成额外的代码。这些额外的代码主要包括以下内容
\begin{enumerate}
    \item 信号和槽机制：为了能够使用信号和槽，类必须继承自 \verb|QObject| 并包含 \verb|Q_OBJECT| 宏。moc 会生成必要的代码来处理信号和槽的连接和调用。
    \item 运行时类型信息(RTTI)：使用 \verb|Q_OBJECT| 宏的类可以获得运行时类型信息，可以通过 \verb|qobject_cast| 进行类型安全的转换。例如，你可以使用 \verb|qobject_cast| 将一个 \verb|QObject| 指针安全地转换为其实际的子类类型。
    \item 国际化支持
    \item 属性系统：类可以定义属性(Properties)，这些属性可以在运行时访问和修改。moc 会生成相应的 \verb|getter| 和 \verb|setter| 方法。
    \item 对象名称和对象树：类可以设置和获取对象的名称(通过 \verb|setObjectName| 和 \verb|objectName| 方法)，并且可以管理对象树(通过 \verb|parent| 和 \verb|children| 方法)。
\end{enumerate}

\paragraph{QApplication对象}
\verb|QApplication| 是 Qt 框架中的一个类，用于管理图形用户界面GUI应用程序的控制流和主要设置。它是每个使用 Qt 开发的 GUI 应用程序的核心部分。在创建任何其他 Qt 对象之前，通常需要先创建 \verb|QApplication|对象。

\verb|QApplication|对象的主要功能有以下内容
\begin{enumerate}
    \item 事件处理：它管理事件循环，接收来自操作系统的输入事件(如鼠标点击或键盘输入)，并将这些事件分发给相应的窗口小部件(widgets)进行处理。
    \item 初始化：它初始化资源，如字体和颜色数据库，并设置应用程序的整体外观风格。
    \item 会话管理：支持与桌面环境的交互，比如在关闭应用程序时保存状态，以便下次启动时恢复。
    \item 国际化：提供对多语言的支持，使应用程序可以轻松地适应不同的语言和地区设置。
    \item 命令行参数：处理传递给应用程序的命令行参数，这可以通过构造函数完成。
\end{enumerate}

\subsection{QMainWindow}
用于创建主窗口应用程序。它提供了一个标准的框架，包括菜单栏、工具栏、状态栏和中心部件区域。使用 \verb|QMainWindow| 可以快速构建出符合常见桌面应用布局的应用程序。

主要包含的内容有
\begin{enumerate}
    \item 菜单栏(Menu Bar)：位于窗口的顶部，用于放置各种菜单项。
    \item 工具栏(Tool Bars)：可以放置按钮、组合框等控件，方便用户快速访问常用功能。
    \item 状态栏(Status Bar)：位于窗口的底部，用于显示临时消息或状态信息。
    \item 中心部件(Central Widget)：占据窗口的主要区域，用于放置主要内容，如文本编辑器、图表、浏览器等。
    \item 停靠窗口(Dock Widgets)：可以浮动或停靠在主窗口的边缘，用于显示辅助信息或工具。
\end{enumerate}

\subsection{QAction}
\verb|QAction|用于表示应用程序中的命令动作。

这些动作可以关联到菜单项、工具栏按钮、键盘快捷键等用户界面元素上。\verb|QAction| 提供了一种统一的方式来处理这些不同类型的用户交互，使得代码更加模块化和易于维护。

\verb|QAction|可以发送信号，用于激活一些已被绑定了的槽函数。

\verb|QAction|可以设置图标和文本，可以设置快捷键，提供状态栏提示\verb|setStatusTip()|和工具提示\verb|setToolTip()|。

对于那些可以被选中或取消选中的动作，\verb|QAction| 支持设置检查状态(checked/unchecked)

\begin{lstlisting}
#include <QApplication>
#include <QMainWindow>
#include <QAction>
#include <QMenuBar>
#include <QToolBar>

int main(int argc, char *argv[])
{
    QApplication app(argc, argv);
    
    QMainWindow window;
    window.setWindowTitle("QAction Example");
    
    // 创建一个 QAction
    QAction *action = new QAction("&Exit", &window);
    action->setShortcut(QKeySequence::Quit); // 设置快捷键
    action->setStatusTip("Exit the application"); // 设置状态提示
    connect(action, &QAction::triggered, &app, &QApplication::quit); // 连接信号与槽
    
    // 添加到菜单栏
    QMenu *fileMenu = window.menuBar()->addMenu("&File");
    fileMenu->addAction(action);
    
    // 添加到工具栏
    QToolBar *toolBar = window.addToolBar("Main Toolbar");
    toolBar->addAction(action);
    
    window.show();
    return app.exec();
}
\end{lstlisting}

\subsection{QKeySequence}
\verb|QKeySequence| 是 Qt 框架中的一个类，用于表示一个或多个键盘按键组合(即快捷键)。它主要用于在应用程序中定义和处理键盘快捷键，使得用户可以通过特定的键组合快速执行某些操作。

\paragraph{构造函数}
\begin{lstlisting}
// 从字符串创建快捷键序列
QKeySequence(const QString &key, Format format = PortableText)
// 从键值创建快捷键序列
QKeySequence(int key1, int key2 = 0, int key3 = 0, int key4 = 0)
\end{lstlisting}

\paragraph{静态方法}
\begin{lstlisting}
// 从字符串创建快捷键序列
QKeySequence::fromString(const QString &str, Format format = PortableText)
// 将快捷键序列转换为字符串
QKeySequence::toString(Format format = PortableText) const
// 获取标准快捷键的键绑定列表
QKeySequence::keyBindings(StandardKey key)
\end{lstlisting}

\paragraph{检测快捷键}
在事件处理中，可以使用 \verb|QKeySequence| 来检测用户是否按下了某个特定的快捷键组合
\begin{lstlisting}
void MainWindow::keyPressEvent(QKeyEvent *event) {
    if (event->matches(QKeySequence::Quit)) {
        close();  // 如果用户按下了退出快捷键，关闭窗口
    }
}
\end{lstlisting}

\subsection{QWidget}
\verb|QWidget| 是 Qt 框架中的一个基础类，用于创建和管理所有的用户界面组件。几乎所有可见的 UI 元素都是从 \verb|QWidget| 派生出来的，包括按钮、标签、文本框、滑块等。\verb|QWidget| 本身是一个空白的矩形区域，可以用来绘制图形或放置其他小部件(widgets)。

\paragraph{功能}
\begin{enumerate}
    \item 容器功能：\verb|QWidget| 可以作为其他小部件的容器。通过将小部件设置为某个 \verb|QWidget| 的子对象，可以方便地管理和布局这些小部件。
    \item 事件处理：\verb|QWidget| 提供了丰富的事件处理机制，可以处理鼠标点击、键盘输入、窗口重绘等各种事件。
    布局管理：
    \item \verb|QWidget| 支持多种布局管理器(如 \verb|QVBoxLayout|、\verb|QHBoxLayout|、\verb|QGridLayout|等)，可以方便地对子小部件进行布局。
    \item 样式表：\verb|QWidget| 支持 CSS 样式表，可以使用类似于 \verb|HTML|、\verb|CSS| 的语法来定制小部件的外观。
    \item 绘图功能：\verb|QWidget| 提供了绘图功能，可以通过重写 \verb|paintEvent| 方法来实现自定义绘图。
\end{enumerate}

\subsection{QDialog}
对话框

对话框是一种特殊的窗口，通常用于与用户进行简短的交互，例如获取输入、显示信息或确认操作。\verb|QDialog| 可以是模态的(modal)或非模态的(non-modal)

\begin{enumerate}
    \item 模态对话框：模态对话框会阻止用户与应用程序的其他部分交互，直到对话框被关闭。这对于需要用户立即做出响应的情况非常有用。
    \item 非模态对话框：非模态对话框允许用户在对话框打开时继续与应用程序的其他部分交互。这对于不需要立即响应的情况非常有用。
    \item 标准对话框：Qt 提供了一些常用的预定义对话框，如 \verb|QFileDialog|(文件对话框)、\verb|QMessageBox|(消息对话框)、\verb|QColorDialog|(颜色对话框)等，这些对话框都是基于 \verb|QDialog| 实现的。
    \item 自定义对话框：您可以继承 \verb|QDialog| 来创建自定义对话框，添加自己的控件和逻辑。
\end{enumerate}

\subsection{QStringList}
存储和操作字符串列表

它是一个专门针对 \verb|QString|(Qt 的字符串类)设计的动态数组，提供了许多方便的方法来处理字符串列表。

\verb|QStringList| 继承自 \verb|QList<QString>|，因此它具有 \verb|QList| 的所有功能，并添加了一些特定于字符串的操作。

\section{事件}
\subsection{QEvent}
\verb|QEvent|是所有事件的基类，用于封装各种类型的应用程序事件。

\verb|type()|返回事件的类型，是一个枚举量

\subsection{QMouseEvent}
鼠标事件，如按下、释放或移动鼠标等。
\begin{enumerate}
    \item \verb|button()|返回触发事件的鼠标按钮。
    \item \verb|buttons()|返回所有被按下的鼠标按钮的组合。
    \item \verb|pos()|返回相对于接收事件的控件的位置。
    \item \verb|globalPos()|返回相对于屏幕的位置。
\end{enumerate}

\subsection{QEnterEvent}
\verb|QEnterEvent| 类提供了鼠标指针进入一个控件时的信息。当鼠标指针从一个控件外部进入该控件内部时，会触发 \verb|enterEvent| 函数，并传递一个 \verb|QEnterEvent| 对象给它。虽然 \verb|QEnterEvent| 继承自 \verb|QMouseEvent|，但它主要用于检测鼠标是否进入了某个特定的区域，而不涉及实际的鼠标点击或拖动。
\begin{enumerate}
    \item \verb|pos()|返回鼠标指针进入控件时的位置(相对于控件)
    \item \verb|globalPos()|返回鼠标指针进入控件时的位置(相对于屏幕)
\end{enumerate}

\section{通信}
\subsection{信号与槽机制}
\paragraph{信号(signals)}
在Qt框架中，信号(Signals)和槽(Slots)是实现对象间通信的核心机制。

当特定的事件发生时，比如用户点击了一个按钮，对象会发出一个\textbf{信号}。

信号可以带有参数，这些参数用于传递与该事件相关的信息。

信号是在类中声明的，通常在头文件中使用\verb|signals:|关键字定义。信号函数没有返回值，因为它们的作用仅仅是通知其他对象发生了某件事情。

\begin{lstlisting}
signals:
    void SendTabIndex(int major, int minor);
    void SendShowPosition(int position);
\end{lstlisting}

\paragraph{槽(slots)}
槽是普通的成员函数，可以被直接调用，也可以通过信号触发。当一个信号被发射时，与之相连的槽会被自动调用。

槽可以在类中使用\verb|slots:|关键字定义。槽可以有返回值，也可以接受参数。槽的设计允许它们做任何普通成员函数能做的事情。

\begin{lstlisting}
public slots:
    void RxSelectDevice(const QString &key);
    void RxOpenDbInfo(const QString &time, const QString &name, const QString &comm);
\end{lstlisting}

\paragraph{信号与槽的关系}
一旦一个信号被发射，所有连接到这个信号的槽函数都会被调用，顺序不定。

同一个槽可以连接到多个信号，而不同的槽也可以连接到同一个信号。

使用\verb|QObject::connect()|函数可以将信号和槽连接起来。这个函数需要四个参数：发送者对象、信号、接收者对象和槽。
\begin{lstlisting}
// 假设有一个QPushButton对象button和一个槽函数handleButtonClicked()
connect(button, &QPushButton::clicked, this, &MyClass::handleButtonClicked);
\end{lstlisting}

\paragraph{信号与槽机制的优势}
\begin{enumerate}
    \item 解耦：信号和槽机制有助于降低组件之间的耦合度，因为对象不需要知道它所发出的信号会被哪个对象处理。
    \item 灵活性：可以动态地添加或移除连接，这为应用程序提供了极大的灵活性。
    \item 多播：一个信号可以连接到多个槽，或者一个槽可以连接到多个信号。
\end{enumerate}

\paragraph{注意事项}
\begin{enumerate}
    \item 在使用信号和槽之前，确保你的类继承自QObject，并且使用了\verb|Q_OBJECT|宏。
    \item 信号和槽的连接可以跨线程进行，但是需要特别注意线程安全问题。
    \item 信号和槽机制虽然强大，但过度使用可能会导致程序结构不清晰，因此应该合理设计信号和槽的使用。
\end{enumerate}

\subsection{属性机制}
属性系统允许对象拥有动态的属性，这些属性可以被读取、写入和绑定。

属性系统通常与信号和槽一起使用，可以实现属性变化时的自动通知。

\subsection{元对象系统}
元对象系统是 Qt 的一个核心特性，提供了反射和元编程的能力。通过元对象系统，可以在运行时获取类的信息，调用方法，访问属性等。

反射：可以在运行时获取类的名称、基类、方法、属性等信息。

动态调用：可以在运行时动态调用方法。

\subsection{事件系统}
事件系统是 Qt 用于处理用户输入、窗口系统事件等的一种机制。事件可以由操作系统生成，也可以由应用程序生成。事件处理器(Event Handlers)可以捕获和处理这些事件。

灵活性：可以自定义事件类型和处理逻辑。

层次结构：事件可以传递给父对象，形成事件的传播链。

\subsection{线程间通信}
线程间通信是 Qt 用于在不同线程之间传递数据和控制信息的机制。Qt 提供了多种线程同步和通信的方法，包括信号与槽、互斥锁(Mutex)、条件变量(Condition Variable)等。

线程安全：信号与槽机制在多线程环境中是线程安全的。

同步：可以使用互斥锁、条件变量等同步原语来协调线程间的操作。

\chapter{libevent - 通信}
\paragraph{event\_base}
\verb|event_base| 是 libevent 库中的一个核心类型，它代表了一个事件循环(event loop)。

\verb|event_base|是一种Reactor框架。

\verb|event_base|可以用来管理许多事件，并且在单个线程中运行一个或者多个事件循环。\verb|event_base|本身不是线程安全的，但是libevent提供其他工具以支持多线程。

\paragraph{event}
\verb|event|类型，代表的就是事件。

如果把\verb|event_base|比作一棵树，那么\verb|event|就是树上的果子。

\chapter{ZeroMQ - 通信}
\minitoc
\section{安装ZMQ}
zmq对C++提供多种实现，其中，最基础、最底层的是libzmq。轻度封装的header-only的cppzmq，高度封装的zmqpp。

cppzmq 与其他 C++ 绑定(如 zmqpp、czmqpp 和 fbzmq)的主要区别在于它是轻量级的，仅通过头文件提供绑定，而其他绑定则可能提供更高层次的功能或需要特定的编译器版本支持。

编译构建libzmq

先运行autogen.sh脚本，生成configure文件

运行./configure，生成make文件

运行./make进行构建

make install时，如果返回permission denied，是因为make install默认是要安装到/usr/local/lib中(实际上这个默认安装目录是在生成configure脚本时就确定了的，并非是make文件所设置的)。而在/usr/local/lib中写文件需要sudo权限。

make后，除了往/usr/local里面安装了一些静态库、动态库和头文件之外，还生成了/usr/local/lib/libzmq.la和/usr/local/lib/pkgconfig/libzmq.pc这两个文件。这两个文件都是纯文本文件。

libzmq.la 文件是一个 Libtool 库文件，主要用于支持 Libtool 工具链。

libzmq.pc 文件是一个 pkg-config 文件，用于提供库的编译和链接选项。pkg-config 是一个帮助程序，用于查询已安装库的编译和链接标志。它广泛用于 Unix-like 系统上的软件开发，特别是那些需要链接多个库的复杂项目。

\section{基本通信模式}
\paragraph{请求-应答模式 server-client模式}
在创建\verb|zmq::socket_t|时，要传入\verb|ZMQ_REP|和\verb|ZMQ_REQ|参数。

\verb|ZMQ_REQ|套接字遵循严格的请求-响应模式。因此，在实际应用中，确保在发送下一个请求之前已经收到了前一个请求的响应是很重要的。

\paragraph{发布-订阅模式 pub-sub模式}
订阅者的\verb|recv()|也默认是阻塞式的。
发送\verb|zmq::message_t|类型时，不要有空的字符成员，否则可能会解读为意想不到的字符。发布者在填入消息时，不要手动添加\verb|’\0’|。

Pub-sub 就像电台广播一样，在你加入之前，你会错过所有的信息，然后你能获得多少信息取决于你的接收质量。

pub-sub模式可能失效的情况：
\begin{enumerate}
    \item 订阅者加入晚了，因此错过了服务器已经发送的信息。
    \item 订阅者获取信息的速度太慢，导致队列堆积，然后溢出。
    \item 订阅者不在时可能会退出并丢失信息。
    \item 订阅者可能会崩溃并重新启动，从而丢失已收到的数据。
    \item 网络可能超载并丢失数据(特别是 PGM)。
    \item 网络可能变得太慢，导致发布者队列溢出，发布者崩溃。
\end{enumerate}

pub和sub之间，也可以有同步机制。

\paragraph{HWM机制(High-Water Marks)}
zmq使用HWM来定义他内部缓冲区的大小。

有些套接字(PUB、PUSH)只有发送缓冲区。有些套接字(SUB、PULL、REQ、REP)只有接收缓冲区。有些套接字(DEALER、ROUTER、PAIR)既有发送缓冲区，也有接收缓冲区。

在 ZeroMQ v2.x，HWM 默认为无限。在 ZeroMQ v3.x 中，默认设置为 1,000

HWM 并不精确；默认情况下，您可能会收到多达 1,000 条消息，但由于 libzmq 实现队列的方式不同，实际缓冲区大小可能会小得多(甚至只有一半)

当套接字达到其 HWM 时，它会根据套接字类型阻塞或丢弃数据。PUB 和 ROUTER 套接字在达到其 HWM 时会丢弃数据，而其他套接字类型则会阻塞。通过 inproc 传输，发送方和接收方共享相同的缓冲区，因此真正的 HWM 是双方设置的 HWM 之和。

\paragraph{与多线程编程}
需要注意的地方
\begin{enumerate}
    \item 在其线程内私自隔离数据，绝不在多个线程中共享数据。唯一的例外是 ZeroMQ 上下文，它是线程安全的。
    \item 远离经典并发机制，如互斥、临界区段、信号等。在 ZeroMQ 应用程序中，这些都是反模式。
    \item 在进程开始时创建一个 ZeroMQ 上下文，并将其传递给希望通过 inproc 套接字连接的所有线程。
    \item 使用附加线程在应用程序中创建结构，并通过 inproc 使用 PAIR 套接字将这些线程连接到其父线程。其模式是：绑定父套字，然后创建子线程连接其套字。
    \item 使用分离线程来模拟独立任务，它们有自己的上下文。通过 tcp 连接这些线程。之后，你可以将这些线程转移到独立进程中，而无需大幅修改代码。
    \item 线程之间的所有交互都以 ZeroMQ 消息的形式发生，你可以或多或少地对其进行正式定义。
    \item 不要在线程之间共享 ZeroMQ 套接字。ZeroMQ 套接字不是线程安全的。从技术上讲，可以将套接字从一个线程迁移到另一个线程，但这需要一定的技巧。线程间共享套接字的唯一合理之处在于需要在套接字上进行垃圾回收等操作的语言绑定。
\end{enumerate}

\section{多部分消息}
\paragraph{信封机制(Pub-Sub Message Envelopes)}
订阅会进行前缀匹配。也就是说，它们会查找"所有以 XYZ 开头的信息"

使用信封，匹配不会跨越帧边界。

\paragraph{多部分消息(multipart messages)}
单部分消息 vs. 多部分消息：

在 ZeroMQ 中，一个消息可以由一个或多个部分组成。

每个部分是一个独立的 \verb|zmq_msg_t|，但它们作为一个整体被原子地交付，即接收方要么接收全部部分，要么一个也不接收。

多部分消息的发送：

发送多部分消息时，每发送一个部分，需要告诉套接字还有更多的部分将要发送。

最后一个部分发送时，不再标记为 \verb|more|，而是使用 \verb|zmq::send_flags::none|。

发送多部分消息，有如下的代码示例
\begin{lstlisting}
zmq::message_t msg1, msg2;
auto res = sock.send(msg1, zmq::send_flags::sndmore);
res = sock.send(msg2, zmq::send_flags::none);
\end{lstlisting}

接收多部分消息，需要多次调用 \verb|recv()|，并在每次接收后检查是否有更多的部分。使用 \verb|msg1.more()| 方法来检查是否还有更多部分。
\begin{lstlisting}
zmq::message_t msg1, msg2;
auto res = sock.recv(msg1, zmq::recv_flags::none);
if (!msg1.more()) {
    std::cout << "Oh dear, sheeps are going astray\n";
} else {
    auto res = sock.recv(msg2, zmq::recv_flags::none);
}
\end{lstlisting}

\paragraph{多部分消息的注意事项}
\begin{enumerate}
    \item 当您发送多部分消息时，只有当您发送最后一部分时，第一部分(以及所有后续部分)才会在线路上实际发送。
    \item 如果您使用的是 \verb|zmq_poll()|，那么当您收到消息的第一部分时，所有其他部分也已到达。
    \item 您将收到消息的所有部分，或者根本没有收到任何部分。
    \item 消息的每一部分都是一个单独的 \verb|zmq_msg| 项目。
    \item 无论您是否检查了 \verb|more| 属性，您都将收到消息的所有部分。
    \item 发送时，ZeroMQ 会将消息帧在内存中排队，直到收到最后一个，然后再将它们全部发送出去。
    \item 除非关闭套接字，否则无法取消部分发送的消息。
\end{enumerate}

\paragraph{带有\_msg\_中缀的接口}
\verb|zmq_send()| 和 \verb|zmq_recv()| 方法是收发数据最简单的办法，但是缺陷在于他需要手动给出缓冲区大小，如果数据大于预定义的缓冲区则会发生截短。

则，zmq还有一种直接面向消息的收发API，即\verb|zmq_msg_XXX()|等接口
\begin{enumerate}
    \item 初始化一条消息：\verb|zmq_msg_init()|、\verb|zmq_msg_init_size()|、\verb|zmq_msg_init_data()|
    \item 发送和接收消息：\verb|zmq_msg_send()|、\verb|zmq_msg_recv()|
    \item 释放消息：\verb|zmq_msg_close()|
    \item 访问消息内容：\verb|zmq_msg_data()|、\verb|zmq_msg_size()|、\verb|zmq_msg_more()|
    \item 处理消息属性：\verb|zmq_msg_get()|、\verb|zmq_msg_set()|
    \item 消息操作：\verb|zmq_msg_copy()|、\verb|zmq_msg_move()|
\end{enumerate}

\paragraph{使用zmq\_msg\_XXX()接口}
您创建并传递的是 \verb|zmq_msg_t| 对象，而不是数据块。

要读取一条信息，您需要使用 \verb|zmq_msg_init()| 创建一条空信息，然后将其传递给 \verb|zmq_msg_recv()|

要从新数据中写入一条信息，需要使用 \verb|zmq_msg_init_size()| 创建一条信息，同时分配一定大小的数据块。然后使用 \verb|memcpy| 填充数据，并将消息传递给 \verb|zmq_msg_send()|

要释放(而不是销毁)一条消息，需要调用 \verb|zmq_msg_close()|。这会丢弃一个引用，最终 ZeroMQ 会销毁该消息。

要访问消息内容，需要使用 \verb|zmq_msg_data()|。要知道消息包含多少数据，请使用 \verb|zmq_msg_size()|。

请勿使用 \verb|zmq_msg_move()|、\verb|zmq_msg_copy()| 或 \verb|zmq_msg_init_data()|，除非您阅读了手册并确切知道为什么需要这些函数。

向 \verb|zmq_msg_send()|传递消息后，ZMQ 将清空消息，即把大小设为零。同一消息不能发送两次，发送后也不能访问消息数据。

如果使用 \verb|zmq_send()| 和 \verb|zmq_recv()|，这些规则就不适用了。

\paragraph{一次创建，多次发送}
如果您想多次发送同一条信息，而且信息量较大，可以创建第二条信息，使用 \verb|zmq_msg_init()|对其进行初始化，然后使用 \verb|zmq_msg_copy()| 创建第一条信息的副本。这不是复制数据，而是复制引用。这样，您就可以发送两次(或更多次，如果您创建了更多副本的话)消息，只有在发送或关闭最后一个副本时，消息才会被最终销毁。

\paragraph{其他关于消息的注意事项}
\begin{enumerate}
    \item 您可以发送零长度的消息，例如从一个线程向另一个线程发送信号。
    \item ZeroMQ 保证发送消息的所有部分(一个或多个)，或者不发送任何部分。
    \item ZeroMQ 不会立即发送消息(单部分或多部分)，而是在以后某个不确定的时间发送。因此，多部分消息必须适合内存。
    \item 消息(单部分或多部分)必须占用内存。如果要发送任意大小的文件，则应将其分成若干部分，并将每部分作为单独的单部分报文发送。使用多部分数据不会减少内存消耗。
    \item 在作用域关闭时不会自动销毁对象的语言中，必须在完成接收信息时调用此方法。在发送信息后不能调用此方法。
\end{enumerate}

\section{其他套接字}
\paragraph{XPUB和XSUB套接字}
中介和代理(Intermediaries and Proxies)

网络拓扑中，如果新增了成员，或者成员频繁增减，他们如何知晓各自的存在。动态

zeromq还是依赖于一个中间节点，他知道所有的节点的存在，所有的节点只需要找到他就能找到其他所有人。

XPUB和XSUB套接字，他是一种专门处理订阅关系的pub-sub套接字。

\paragraph{DEALER和ROUTER套接字}
服务必须是无状态的，他不能依赖于之前的服务，他的一切信息必须存放在当前报文中，或者至少存放在一个共享的位置。

多个client和多个server之间如何连接的问题。

可以手动配置所有的client和server的zmq套接字，但是如果要增加1台主机，则需要修改所有与之有关的主机里面的zmq套接字的连接信息。

这就是静态配置的缺陷，所以需要动态。

DEALER 和 ROUTER 的套接字可以让你实现非阻塞的请求-响应

在这个简单的扩展请求-回复模式中，REQ 与 ROUTER 对话，DEALER 与 REP 对话。

使用请求-回复代理可以让客户机/服务器架构更容易扩展，因为客户机看不到工作者，工作者也看不到客户机。中间的代理是唯一的静态节点。

\paragraph{从多个套接字接收数据}
要同时从多个套接字读取数据，请使用 \verb|zmq_poll()|。更好的方法可能是将 \verb|zmq_poll()| 打包到一个框架中，将其变成一个漂亮的事件驱动反应器

\paragraph{网络桥接}
解决的是如何将zeromq网络与另一个协议的网络连接起来

答案是，创建一个桥接器，同时使用2种套接字、2种协议。

\section{多路复用zmq\_poll，zmq::poller\_t}
\verb|zmq::poller| 是 ZeroMQ C++ API 提供的一个工具，用于实现多路复用的 I/O 操作。

在C语言层面是\verb|zmq_poll|

\paragraph{基本使用步骤}
\begin{enumerate}
    \item 创建 \verb|zmq::poller_t| 实例。
    \item 调用\verb|add()|向 poller 添加套接字及其对应的事件处理逻辑。
    \item 调用 \verb|poller.wait()| 等待事件的发生。
    \item 根据事件类型处理发生的事件。
\end{enumerate}

\paragraph{线程安全}
\verb|zmq::poller|本身不是线程安全的。

\paragraph{zmq\_poll()}
\begin{lstlisting}
int zmq_poll (zmq_pollitem_t *items, int nitems, long timeout);
\end{lstlisting}

第1和第2个参数传入了1个\verb|zmq_pollitem_t|数组。

如果没有任何事件发生，则在等待\verb|timeout|毫秒之后返回。如果\verb|timeout|是0，则立即返回。如果\verb|timeout|是-1则会阻塞，无限等待。

\paragraph{zmq\_pollitem\_t结构}
\verb|zmq_pollitem_t|的结构如下所示
\begin{lstlisting}
typedef struct
{
    void *socket;
    int fd;
    short events;
    short revents;
} zmq_pollitem_t;
\end{lstlisting}

\verb|socket|指向一个socket，\verb|fd|存储一个文件描述符。如果\verb|socket|和\verb|fd|都设置了值，则\verb|socket|会优先使用。

\verb|events|和\verb|revents|是通过\verb|OR|运算组合以下运算标志位构建的位掩码
\begin{enumerate}
    \item \verb|ZMQ_POLLIN|：对于 ZeroMQ 套接字：表示至少有一条消息可以从套接字接收而不阻塞。对于标准套接字：等同于 \verb|poll()| 系统调用中的 \verb|POLLIN| 标志，通常意味着可以从文件描述符 \verb|fd| \textbf{读取至少一个字节的数据而不阻塞}。
    \item \verb|ZMQ_POLLOUT|：对于 ZeroMQ 套接字：表示至少有一条消息可以发送到套接字而不阻塞。对于标准套接字：等同于 \verb|poll()| 系统调用中的 \verb|POLLOUT| 标志，通常意味着可以向文件描述符 \verb|fd| \textbf{写入至少一个字节的数据而不阻塞}。
    \item \verb|ZMQ_POLLERR|：对于标准套接字：此标志会传递给底层的 \verb|poll()| 系统调用，通常表示指定文件描述符 \verb|fd| 的套接字上\textbf{存在某种错误条件}。对于 ZeroMQ 套接字：如果在 \verb|events| 中设置了此标志，则它不会产生任何效果，并且 \verb|zmq_poll()| 永远不会在 \verb|revents| 中返回这个标志。
\end{enumerate}

\part{音视频开发}
\chapter{基础知识}
\minitoc
\section{常用术语}
\paragraph{复用/解复用(mux/demux)}
把不同的流按照某种容器的规则放入容器，这种行为叫做复用(mux)；把不同的流从某种容器中解析出来，这种行为叫做解复用(demux)。

\paragraph{抗丢包率}
抗丢包率(Packet Loss Resilience, PLR)是指一个网络通信系统或应用在面对数据包丢失时，仍能保持其功能和性能的能力。

提升抗丢包能力，往往有以下几种方法
\begin{enumerate}
    \item 前向纠错 (FEC, Forward Error Correction)：发送额外的冗余信息，使得接收端可以在不请求重传的情况下纠正一定数量的数据包丢失。适用于实时应用，如VoIP和视频流媒体，因为这些应用对延迟敏感，无法承受长时间的重传等待。
    \item 自动重传请求 (ARQ, Automatic Repeat reQuest)：接收端检测到丢失的数据包后，向发送端发出重传请求。适合非实时应用，因为它可能导致较高的延迟，但可以确保数据完整性和准确性。
    \item 冗余编码：类似于FEC，但可能涉及更复杂的编码方案，例如使用喷泉码(fountain codes)来生成无限量的编码包，只要接收到足够数量的编码包就可以解码出原始数据。
    \item 交织 (Interleaving)：将数据分散到多个数据包中，即使某些包丢失，也可以通过其他包重建原始数据。
    \item 自适应比特率 (ABR, Adaptive Bitrate)：根据网络状况动态调整视频流的质量(如分辨率、帧率或编码效率)，以减少丢包的可能性。
    \item 多路径传输：利用多个网络路径来发送数据，增加数据到达目的地的概率。
\end{enumerate}

\paragraph{其他未扩展的术语}
\begin{enumerate}
    \item RTC(Real time communication)实时通信
    \item IM即时通讯(Instant Messaging)
    \item SDL(Simple DirectMedia Layer)是一个跨平台的多媒体开发库，主要用于处理图形、音频、输入设备(如键盘、鼠标、游戏手柄)等
\end{enumerate}

\section{音频格式}
\subsection{PCM}
PCM是一种音频的格式，全称是Pulse Code Modulation，脉冲编码调制。

PCM的工作流程，包括采样(Sampling)，量化(Quantization)，编码(Encoding)这三个环节。

量化(Quantization)：每个采样点的幅度值被映射到最近的可用数值级别。量化过程会引入误差，称为量化噪声。为了减少这种误差，可以增加量化级别(即提高位深度)，如常见的 8 位、16 位或 24 位 PCM。(这也就是16位、24位的出处)

\paragraph{PCM文件的特点}
\begin{enumerate}
    \item 未压缩：PCM 是一种无损编码方式，不涉及任何压缩算法，因此能够保持原始音频的质量，但文件体积较大。
    \item 简单结构：PCM 数据没有复杂的头部信息或元数据，基本上是裸数据，使得它非常适合用作其他更复杂音频格式的基础。
    \item 多种格式支持：许多音频文件格式支持 PCM 编码的数据，比如 WAV、AIFF 等。这些格式通常会在文件头中包含有关采样率、位深度等参数的信息。
\end{enumerate}

\paragraph{常见的PCM参数}
\begin{enumerate}
    \item 采样率(Sample Rate)：每秒钟采集多少个样本，单位为 Hz 或 kHz。常见的有 8kHz(电话质量)、44.1kHz(CD 质量)、48kHz(DVD 质量)等。
    \item 位深度(Bit Depth)：每个样本用多少比特来表示，决定了音量范围内的精度。常见的有 8-bit、16-bit 和 24-bit。
    \item 声道数(Channels)：单声道(Mono)还是立体声(Stereo)。立体声音轨会有两个独立的声道，分别对应左耳和右耳。
\end{enumerate}

\subsection{ACC}
PCM(Pulse Code Modulation，脉冲编码调制)与AAC(Advanced Audio Coding，高级音频编码)是两种不同的音频编码技术

PCM未压缩，文件较大，AAC是有损压缩格式，文件更小。

AAC有两种常见封装格式，ADTS(Audio Data Transport Stream)和ADIF(Audio Data Interchange Format)。其中，ADTS每帧都有一个头部，允许随机访问和流式处理；而ADIF仅在文件开头有一个全局头部，所以必须得到全部的数据之后再解码。

ADTS支持在这个流中的任何一个位置开始。这个特征类似于mp3数据流格式。

\subsection{WAV}
wav格式，是一种未编码、未压缩的音频格式。任何能够直接操作PCM文件的方法都能用来操作WAV文件，因为里面都是未压缩的音频原始数据。

对于未压缩的wav文件，也就不需要什么编码、解码器去操作他。

\paragraph{wav文件格式}
wav文件的主要由以下几个部分组成，可见图\ref{wav文件格式}

这个结构，在编程时，可以建立一个结构体方便管理，而非手动去写里面的一个个字节。
\begin{itemize}
    \item RIFF，4个字节，固定为4个字母RIFF。
    \item ChunkSize ，4个字节，小端序，从这里到文件结尾的字节数(不包括RIFF和ChunkSize这8个字节)
    \item WAVE，4个字节，固定为4个字母WAVE。
    \item fmt，4个字节，固定为fmt3个字母和1个空格。
    \item Subchunk1Size，4个字节，小端序，对于PCM格式，此字段为16，表示这个子块的大小。
    \item AudioFormat，2个字节，小端序，音频格式代码
    \begin{itemize}
        \item 1: PCM
        \item 3: IEEE浮点数
        \item 6: A-Law
        \item 7: μ-Law
        \item 0xFFFE: 压缩格式
    \end{itemize}
    \item NumChannels，2个字节，小端序，声道数，1 为单声道，2 为立体声等。
    \item SampleRate，4个字节，小端序，采样率，例如44100 Hz。
    \item ByteRate，4个字节，小端序，每秒的字节数，计算公式为\[ \text{采样率}SampleRate * \text{通道数}NumChannels * \text{样本位数}BitsPerSample / 8 \]
    \item BlockAlign，2个字节，小端序，数据块对齐，计算公式为\[ \text{通道数}NumChannels * \text{样本位数}BitsPerSample / 8 \]
    \item BitsPerSample，2个字节，小端序，每个样本的位数，例如16位。
    \item data，4个字节，固定为4个字母data
    \item Subchunk2Size，4个字节，小端序，实际音频数据的字节数。
    \item ... (不定长): 实际的音频数据。
\end{itemize}

\begin{figure}[h]\centering
    \label{wav文件格式}
    \includegraphics[width=0.9\textwidth]{wav文件格式.png}
    \caption{wav文件格式}
\end{figure}

\subsection{其他有压缩的音频格式}
mp3

opus，开源格式，适合于实时通信，常见于*.ogg，*.webm容器中

flac，无损编解码器

\section{视频格式}
\subsection{YUV}
\subsubsection{简介}
YUV是一种颜色编码格式，主要用于视频和图像处理。它不同于我们常见的RGB格式(红、绿、蓝三原色)，而是将亮度信息(Y)与色度信息(U和V)分开存储。

Y(Luminance)：表示亮度或灰度信息，对应于黑白电视的信号。

U和V(Chrominance或者Chroma)：代表色度信息，分别对应蓝色差(Cb或U)和红色差(Cr或V)。它们是相对于亮度值的颜色差异。

\paragraph{YUV与RGB的联系}
YUV数据是怎么来的呢？比如说，我们使用三管彩色摄像机或彩色CCD摄像机进行摄像，摄像后得到RGB数据。

信号Y代表的是亮度数据。

信号U来自于R与Y的差值，信号V来自于B与Y的差值。

最后发送端将亮度Y和色差U、V这三个信号分别进行编码，用同一信道发送出去。这种色彩的表示方法就是所谓的YUV色彩空间表示。

\paragraph{使用YUV能够兼容黑白电视机}
采用YUV色彩空间的重要性是它的亮度信号Y和色度信号U、V是分离的。如果只有Y信号分量而没有U、V分量，那么这样表示的图像就是黑白灰度图像。彩色电视采用YUV空间正是为了用亮度信号Y解决彩色电视机与黑白电视机的兼容问题，使黑白电视机也能接收彩色电视信号

\paragraph{从RGB计算YUV}
信号Y是根据RGB三个信号通过加权求和得到的。
\[ Y = 0.299 \cdot R + 0.587 \cdot G + 0.114 \cdot B \]

这个权重值，考虑到了人眼视觉中RGB三种颜色对亮度的贡献程度不同，敏感程度从高到低分别是绿色、红色和蓝色。

信号U来自于R与Y的差值
\[ U = 0.492 \cdot (B - Y) \]

信号V来自于B与Y的差值
\[ V = 0.877 \cdot (R - Y) \]

\subsubsection{YUV的编码方式}
\paragraph{如果用RGB格式编码一张图片}
假如我们有一张$1280 * 720$大小的图片，就代表，他有$1280 * 720$个像素点，总计921600个像素点。

每个像素点，要分别存储R、G、B三个颜色的值，每个原色占用8个bit，可以表达0-255这么多的数值，那么也就是说每个像素点要占用$8 * 3 = 24$个bit，也就是3个字节。

那么这样的一张图片，就占用了$921600 * 3 = 2764800$个字节，共2.637 MB的空间。

而视频，是由许多个帧组成的，每一帧都是一张图片，比如说电影的帧率是24，每秒要24张图片，如果每张图片都是2.637 MB这么大的图片的话，1s的视频就需要占用63.288 MB空间。

这也是设计了YUV以及后续许多编码器的原因，就是，不能把如此大量的原始数据直接用于视频播放，对硬件软件资源都是极大的浪费。

\paragraph{YUV采样}
YUV的采样格式，主要有以下几种
\begin{enumerate}
    \item YUV 4:4:4 采样
    \item YUV 4:2:2 采样
    \item YUV 4:2:0 采样
\end{enumerate}

YUV采样的目的，也是要存储每一张图片，这一点与RGB相同。只是，他使用不同的方式，存储不同的数据，和使用不同的数据存放方式，来使得最终需要被存储的数据量减少，但是又使得图像不失真。

YUV采样，每个像素点就不再是RGB三个值了，而是YUV三个值了。RGB和YUV的转换关系，在前文已经说过了。

\paragraph{YUV444采样}
YUV444采样，就是每个像素点都完整地保存YUV三个向量的值。

每个像素点占用的空间仍然是3个字节，3个8 bit大小的值。

也就是，从存储占据空间的角度来看，与RGB并无区别。只是做了从RGB到YUV的数学上的转换。

\paragraph{YUV422采样}
YUV422采样，意味着UV分量的采样数量各自都是Y分量的一半。假如有20个像素点，那么Y分量采20个，但是U和V分量各自采10个。

假如我们有下面这样的像素点
\begin{equation*}
    \begin{matrix}
        [Y_0, U_0, V_0]&[Y_1, U_1, V_1]&[Y_2, U_2, V_2]&[Y_3, U_3, V_3]\\
        [Y_4, U_4, V_4]&[Y_5, U_5, V_5]&[Y_6, U_6, V_6]&[Y_7, U_7, V_7]
    \end{matrix}
\end{equation*}

经过422采样会得到
\begin{equation*}
    \begin{matrix}
        [Y_0, U_0]&[Y_1, V_1]&[Y_2, U_2]&[Y_3, V_3]\\
        [Y_4, U_4]&[Y_5, V_5]&[Y_6, U_6]&[Y_7, V_7]
    \end{matrix}
\end{equation*}

这样，是2个$Y_i$分量，公用1对U、V分量。

\paragraph{YUV420采样}
YUV420，并不意味着只采样U分量不采样V分量。

首先，假如我们是逐行采样(这个问题与采样的顺序无关)，那么每一行的Y分量是一定要都采样的，但是U分量和V分量，是交替进行的，假如上一行采样了U分量，那么下一行就不采样V分量

在这个基础上，每一行中，U分量和V分量的采样数量是Y分量的一半

假如我们有下面这样的像素点
\begin{equation*}
    \begin{matrix}
        [Y_0, U_0, V_0]&[Y_1, U_1, V_1]&[Y_2, U_2, V_2]&[Y_3, U_3, V_3]\\
        [Y_4, U_4, V_4]&[Y_5, U_5, V_5]&[Y_6, U_6, V_6]&[Y_7, U_7, V_7]
    \end{matrix}
\end{equation*}

那么按照YUV420方式进行采样，首先每一行的$Y_i$分量一定会被采样进来，得到
\begin{equation*}
    \begin{matrix}
        Y_0 & Y_1 & Y_2 & Y_3 \\ 
        Y_4 & Y_5 & Y_6 & Y_7
    \end{matrix}
\end{equation*}

然后，我们假定第一行采样U分量，第二行采样V分量，并且每一行的U、V分量都是Y分量的一半，我们得到
\begin{equation*}
    \begin{matrix}
        [Y_0, U_0] & Y_1 & [Y_2, U_2] & Y_3 \\ 
        [Y_4, V_4] & Y_5 & [Y_6, V_6] & Y_7
    \end{matrix}
\end{equation*}

这样，是4个$Y_i$分量，公用1对U、V分量，比起422采样，更能减少空间。

前文中举例的那个$1280 * 720$大小的图片，在420采样时所占用的空间，就减少到
\begin{equation*}
    (1280 * 720 * 8 + 1280 * 720 * 0.25 * 8 * 2) / 8 / 1024 / 1024 = 1.32 MB
\end{equation*}

\subsubsection{YUV存储格式}
前面我们聊了采样，就是讲清楚了有哪些数据我们需要存储，哪些数据不需要存储(也不会导致图片失真)。我们有了这么多采样点及其分量，那么如何存储这些数据就是下面的问题。

YUV的存储格式有两种
\begin{itemize}
    \item planar 平面格式：先连续存储所有像素点的 Y 分量，然后存储 U 分量，最后是 V 分量
    \item packed 打包模式：指每个像素点的 Y、U、V 分量是连续交替存储的
\end{itemize}

\paragraph{基于422采样的存储格式}
主要有2种
\begin{enumerate}
    \item YUYV：格式是采用打包格式进行存储的，比如Y0 UO Y1 V0 Y2 U2 Y3 V2
    \item UYVY：和YUYV类似，只是U、V分量放在前面
\end{enumerate}

\paragraph{基于420采样的存储格式}
暂略，详见\href{https://zhuanlan.zhihu.com/p/113122344}{知乎文章 - 跳转原文}

\subsection{H.264}
AVC(Advanced Video Coding)，并不是一种文件格式，而是一种视频压缩标准或编解码器(codec)

它定义了如何对视频进行编码和解码以实现高效的压缩和高质量的图像。

高效压缩：相比之前的视频压缩标准，如 MPEG-2 和 H.263，H.264 提供了更高的压缩率，在保持相同视觉质量的同时显著减少了所需的比特率。

灵活性：支持多种分辨率和帧率，适用于从低带宽移动设备到高清晰度电视的各种应用。

错误恢复能力：设计时考虑到了网络传输中的丢包问题，具有较好的容错性。

\subsection{视频编码格式}
H26x系列(1到5)和MPEG系列(1到4)

H26x系列由ITU国际电信联盟主导，MPEG系列由MPEG主导。

他们联合制定的H264，和H265

H.264又称为AVC

H.265在保证数据真实程度前提下，压缩性能更高。

\paragraph{硬解码与软解码}
软解码就是使用CPU通过软件方式来解码。硬解码是有专门的硬件(比如GPU)来进行解码。

\paragraph{视频容器}
音视频编码数据的容器，把音频和视频数据，混合在一个文件中。

也就是日常生活中接触到的视频文件，以及其后缀名

\subsection{帧，关键帧}
\paragraph{I帧}
I帧(Intra-coded frame)，关键帧，独立编码，不依赖其他帧。包含完整的图像信息，解码时可以直接显示。通常作为 GOP 的起始帧，用于随机访问和错误恢复。

\paragraph{P帧}
P帧(Predictive-coded frame)，预测帧，通过参考前面的 I帧 或 P帧 进行编码。只存储与参考帧的差异信息，压缩率较高。解码时需要依赖前面的帧。

\paragraph{B帧}
B帧(Bi-directional predictive-coded frame)，双向预测帧，通过参考前面的帧和后面的帧进行编码。压缩率最高，但解码时需要前后帧的信息。会增加解码延迟。

\paragraph{GOP}
GOP(Group of Pictures)，指的是一组连续的视频帧，通常以 I帧 开头，后面跟随若干 P帧 和 B帧。GOP 是视频压缩的基本单位，决定了视频的压缩效率、随机访问能力和容错性。

1个典型的GOP的组成，往往由I帧为起始，里面包含许多B帧和P帧，最后以1个I帧结尾，也可能看做是下一个GOP单元的起始。

从图\ref{I帧B帧与P帧}可以看出，可以简单理解为，P帧仅依赖于他前面的帧，而B帧同时依赖于他前面和后面的帧。
\begin{figure}[!ht]\centering
    \caption{I帧B帧与P帧}\label{I帧B帧与P帧}
    \includegraphics[width=0.8\textwidth]{I帧B帧与P帧}
\end{figure}

\paragraph{GOP的作用}
\begin{enumerate}
    \item 通过使用 P帧 和 B帧，GOP 可以显著减少视频文件的大小。I帧 占用空间较大，但 P帧 和 B帧 只存储差异信息，压缩率更高。
    \item 利用I帧可以支持随机访问。I帧 是独立帧，可以作为视频的访问点。在视频播放或编辑时，可以通过 I帧 快速定位到某一时刻。
    \item 提供一定的容错性。如果视频流中丢失了某些B帧或者P帧，那么可以根据下一个即将到来的I帧来恢复
\end{enumerate}

\paragraph{时间冗余与空间冗余}
时间冗余：哪些部分在播放到下一帧时会变化，哪些部分不会变化。对于不会变化的数据，我们不再重复去存储，降低视频文件大小。

空间冗余：这个帧里，哪些部分与其他相邻部分是完全一致的，他们存储了相同的数据，我们也不重复存储这份数据。

\chapter{ffmpeg - 开源音视频工具库}
\minitoc
\subsection{ffmpeg命令行}
\subsubsection{ffmpeg命令的组成}
ffmpeg的命令行，主要可以划分为以下几个部分
\begin{lstlisting}
ffmpeg {1} {2} -i {3} {4} {5}
\end{lstlisting}

\begin{enumerate}
    \item 全局参数
    \item 输入文件参数
    \item 输入文件
    \item 输出文件参数
    \item 输出文件
\end{enumerate}

\subsubsection{常用命令行选项}
\verb|-bf frames|，设置使用B帧的数量，这个数量指的是每2个关键帧(也就是I帧)之间的B帧的数量。比如说\verb|-bf 2|，那么也就是每2个I帧之间最多会出现2个B帧。

\verb|-f fmt|，采用\verb|fmt|给定的格式，比如\verb|-f v4l2|、\verb|-f alsa|

\verb|-i|，指定输入源，比如\verb|-i /dev/video4|、\verb|-i default|

\verb|-c:v vcodec|和\verb|-c:a avodec|是设置视频和音频的编解码器。比如使用H.264格式的视频，\verb|-c:v libx264|。直接复制视频流，不重新编码\verb|-c:v copy|。\verb|-c:a aac|，使用AAC格式的音频。

\verb|-vn|，不处理视频流

\verb|-an|，不处理音频流

\verb|-ac 2|，设置音频的声道数为2。

\verb|-preset|，用于设置编码速度和压缩效率的平衡。\verb|ultrafast|，编码速度最快，压缩效率最低。\verb|veryslow|，编码速度非常慢，压缩效率最高。

\verb|-pix_fmt|，用于设置像素格式。像素格式决定了颜色信息的存储方式和精度。可选的有\verb|yuv420p|、\verb|yuv422p|、\verb|yuv444p|、\verb|rgb24|。其中\verb|rgb24|未压缩的 RGB 格式，适用于图像处理。

\chapter{ASLA - 高级Linux音频架构}
\minitoc
\section{ALSA库}
全称是Advanced Linux Sound Architecture，即高级Linux音频架构。在Linux 2.6的内核版本后，Alsa目前已经成为了linux的主流音频体系结构。

\subsection{aplay}
aplay是ASLA库提供的一个命令行工具，用于播放音频。

可以直接使用，不加任何参数
\begin{lstlisting}
    aplay output.wav
\end{lstlisting}

\paragraph{aplay命令的常用选项}
将aplay命令的常用选项总结如下
\begin{table}[!h]
    \centering
    \caption{aplay命令的常用选项} \label{aplay命令的常用选项}
    \begin{tabular}{|p{15em}|p{30em}|}
        \hline
        选项 & 功能 \\ \hline
        \verb|-c, --channel| & 指定通道数，默认是1，从1到32 \\ \hline
        \verb|-D| & 指定具体的播放设备 \\ \hline
        \verb|-f, --format| & 指定播放的格式 \\ \hline
        \verb|-r, --rate| & 指定采样率 \\ \hline
        \verb|-v, --verbose| & 详细模式，显示更多信息 \\ \hline
    \end{tabular}
\end{table}

\subsection{arecord}
arecord是ASLA库提供的一个命令行工具，用于从声卡录制音频。

最简单的使用方式。这个命令会以默认格式(通常是16位PCM，44.1kHz采样率，立体声)开始录音，并将输出保存到名为 output.wav 的文件中。按 Ctrl+C 可以停止录音。
\begin{lstlisting}
arecord output.wav
\end{lstlisting}

可以使用\verb|-D|选项来指定录音设备
\begin{lstlisting}
arecord -D hw:0,0 output.wav
arecord -D plughw:0,0 output.wav  // 使用插件层
\end{lstlisting}

使用\verb|-r|或者\verb|--rate|来指定采样率
\begin{lstlisting}
arecord -r 48000 output.wav
\end{lstlisting}

\verb|-f|或者\verb|--format|指定录音的位深

\verb|S16_LE|表示16位有符号小端格式。
\begin{lstlisting}
arecord -f S24_LE output.wav
\end{lstlisting}

使用\verb|--duration=n|来指定录音的时长
\begin{lstlisting}
arecord --duration=10 output.wav
\end{lstlisting}

\paragraph{arecord命令的常用选项}
将arecord命令的常用选项总结如下
\begin{table}[!h]
    \centering
    \caption{arecord命令的常用选项} \label{arecord命令的常用选项}
    \begin{tabular}{|p{15em}|p{30em}|}
        \hline
        选项                               & 功能             \\ \hline
        \verb|-c, --channel| & 指定通道数，默认是1，从1到32 \\ \hline
        \verb|--duration=n| & 指定录音的时长 \\ \hline
        \verb|-D| & 指定录音设备 \\ \hline
        \verb|-f, --format| & 指定录音的位深 \\ \hline
        \verb|-r, --rate| & 指定采样率 \\ \hline
        \verb|-t, --file-type| & 指定生成文件的类型，默认是\verb|wav|格式 \\ \hline
        \verb|-v, --verbose| & 详细模式，显示更多信息 \\ \hline
    \end{tabular}
\end{table}

\subsection{ALSA的插件}
\paragraph{插件链}
ALSA允许通过冒号(中间无空格)串联起多个插件，形成处理链。

比如下面的代码示例中，音频会按照原始音频->\verb|plug|(格式转换)->\verb|route|(声道映射)->\verb|dmix|(混音输出)的顺序进行处理。
\begin{lstlisting}
aplay -D plug:route:dmix audio.wav
\end{lstlisting}

\subsubsection{基础硬件访问}
\verb|hw| 或 \verb|default|，直接访问声卡硬件，绕过所有插件(无格式转换或混音)
\begin{lstlisting}
aplay -D hw:0,0 audio.wav  # 直接使用声卡设备0的子设备0
\end{lstlisting}

\subsubsection{混音与多路复用}
\verb|dmix|，软件混音，允许多个应用共享同一输出设备。默认情况下，ALSA的独占模式会导致一次只能有一个应用播放音频，而dmix通过软件混合多个音频流解决这一问题。

配置示例(在 \verb|/etc/asound.conf| 中启用默认混音)
\begin{lstlisting}
pcm.!default {
    type plug
    slave.pcm "dmix"
}
\end{lstlisting}

\verb|dsnoop|，作用与\verb|dmix|类似，但是适用于输入设备(即，麦克风)，允许多个应用同时录制音频。
\begin{lstlisting}
arecord -D dsnoop:0,0 input.wav  # 多应用共享输入设备
\end{lstlisting}

\subsubsection{格式转换与重采样插件}
\verb|plug|，自动转换音频格式(采样率、位深、声道数)，匹配硬件支持的参数。
\begin{lstlisting}
aplay -D plug:SLAVE_DEVICE audio.wav  # 将音频转换后传递给 SLAVE_DEVICE
\end{lstlisting}

\verb|rate|，仅重采样(不处理其他格式转换)，通常与 \verb|plug| 结合使用。
\begin{lstlisting}
aplay -D rate:48000 audio.wav  # 强制重采样到 48kHz
\end{lstlisting}

\subsubsection{路由与声道管理插件}
\verb|route|，重新映射声道或调整声道音量(如将立体声合并为单声道)。

\verb|multi|，将音频输出到多个设备(如同时输出到耳机和HDMI)。

\subsubsection{其他}
\verb|softvol|，负责音量控制，软件音量控制，为不支持硬件音量调节的设备添加虚拟音量控制。

配置示例
\begin{lstlisting}
pcm.softvol {
    type softvol
    slave.pcm "hw:0,0"
    control {
        name "SoftwareVolume"
        card 0
    }
    min_dB -90.0
    max_dB 0.0
}
\end{lstlisting}

\verb|ladspa|，效果处理插件，通过 LADSPA(Linux Audio Developer's Simple Plugin API)加载音频效果(如均衡器、压缩器)。
\begin{lstlisting}
pcm.effect {
    type ladspa
    slave.pcm "hw:0,0"
    path "/usr/lib/ladspa"
    plugins {
        0 {
            label lpf  # 低通滤波器
            input { controls 2000 }  # 截止频率 2kHz
        }
    }
}
\end{lstlisting}

\verb|file|，将音频直接写入文件(如 PCM 裸流)

\verb|shm|，通过共享内存传输音频数据(用于进程间通信)

\verb|pulse|，通过 PulseAudio 输出音频

\verb|jack|，将音频路由到 JACK Audio Connection Kit(专业音频路由工具)

\subsection{alsa库函数}
\paragraph{snd\_pcm\_hw\_params\_get\_period\_size()}
获取 PCM(Pulse Code Modulation)设备的周期(period)大小。
\begin{enumerate}
    \item 缓冲区(buffer): 内存中的音频数据块，用于存储一段时间内的音频数据。
    \begin{itemize}
        \item 缓冲区通常由多个周期组成。
        \item 当硬件的缓冲区填满一个周期的数据后，它就会触发一个中断，通知软件进行处理(如播放或录音完成一部分)。
        \item 缓冲区大小是整个缓冲区中能容纳的样本数或字节数，它等于单个周期大小乘以周期的数量(即缓冲区包含的周期数)。
    \end{itemize}
    \item 周期(period): 缓冲区中的一个子部分，用于分段传输音频数据。周期是音频硬件读写数据的最小单位时间。
    \begin{itemize}
        \item 较小的周期可以减少延迟，但可能增加CPU的中断频率和处理负担
        \item 较大的周期则相反，可以减少CPU的中断，但增加潜在的延迟。
    \end{itemize}
\end{enumerate}

\begin{lstlisting}
int snd_pcm_hw_params_get_period_size(const snd_pcm_hw_params_t *params, snd_pcm_uframes_t *frames, int *dir);
\end{lstlisting}

传入参数
\begin{enumerate}
    \item \verb|params|：指向 \verb|snd_pcm_hw_params_t| 结构体的指针，该结构体包含了 PCM 设备的硬件参数。
    \item \verb|frames|：输出参数，用于接收周期大小(以帧为单位)。帧，代表一个完整采样点的集合，如果是单通道，1个帧就包含1个采样点，如果是n通道，则是n个采样点。帧，可以理解为采样1次得到的结果。2048个帧，那就是采样2048次的结果。
    \item \verb|dir|：输出参数，表示方向。通常可以忽略，传入 \verb|NULL| 即可。
\end{enumerate}

结果存储到\verb|frames|参数中。

成功时返回0，失败返回错误码。

\paragraph{snd\_pcm\_readi()}
从PCM设备读取数据，用于阻塞模式下读取数据，适用于录音需求。
\begin{lstlisting}
ssize_t snd_pcm_readi(snd_pcm_t *pcm, void *buffer, size_t size);
\end{lstlisting}

传入参数
\begin{enumerate}
    \item \verb|pcm|：指向 \verb|snd_pcm_t| 结构体的指针，表示 PCM 设备句柄。
    \item \verb|buffer|：指向缓冲区的指针，用于存储读取到的音频数据。
    \item \verb|size|：要读取的帧数。
\end{enumerate}

返回值，成功，则返回读取到的帧数，失败返回为负数的错误码，错误码是符合POSIX标准的。

\paragraph{snd\_pcm\_writei()}
向PCM设备写入数据，适用于播放需求。
\begin{lstlisting}
ssize_t snd_pcm_writei(snd_pcm_t *pcm, const void *buffer, snd_pcm_uframes_t size);
\end{lstlisting}

传入参数
\begin{enumerate}
    \item \verb|pcm|：指向 \verb|snd_pcm_t| 结构体的指针，表示 PCM 设备句柄。
    \item \verb|buffer|：指向缓冲区的指针，数据格式必须与之前设置的硬件参数相匹配。
    \item \verb|size|：要写入的帧数。每个帧包含所有声道的一个样本。例如，在立体声(双声道)中，一个帧包含左声道和右声道各一个样本。
\end{enumerate}

返回值，如果是非负整数，说明读取成功，返回的是读取到的帧数。如果是负数，是错误码，错误码是符合POSIX标准的。

\subsection{数据类型}
\paragraph{snd\_pcm\_uframes\_t}
用于表示PCM设备中的帧数，他通常是无符号整型类型，比如\verb|unsigned long|或者\verb|unsigned int|，具体实现取决于平台和编译器。

定义在头文件\verb|alsa/asoundlib.h|中

\chapter{PulseAudio - 声音服务器}
\minitoc
\section{PulseAudio简介}
PulseAudio是一个声音服务器，启动时读取配置文件对ALSA系统Control进行配置。作为一个后台进程从一个或多个音源(进程或输入设备)接受声音输入

然后重定向声音到一个或多个槽(声卡，远程网络PulseAudio服务，或其他进程)。PulseAudio的一个目的通过它是重定向所有声音流，包括那些试图访问硬件的进程。

\paragraph{为什么有了ALSA还要有PulseAudio这样的程序}
声卡，是将模拟信号转换为数字信号的硬件设备。ALSA是嵌入linux内核之中的，提供了从声卡读取和写入所需的设备驱动器。

很多程序，都会使用ALSA API来与声卡发生IO，但是会面临\textbf{硬件无法复用}的问题，主要出现于多个进程想要同时从声卡获取当前音频数据的时候。所以，就需要PulseAudio或PipeWire这样的音频服务器。PulseAudio主要能够解决直接使用ALSA时遇到的以下问题
\begin{enumerate}
    \item 同时播放多个应用程序的音频
    \item 它具有高级功能，例如混合多个音频流、音频流、每个应用程序音量控制等
    \item 使用比ALSA更方便
\end{enumerate}

\begin{figure}[!ht]\centering
    \caption{linux系统播放声音时的具体流程}\label{linux系统播放声音时的具体流程}
    \includegraphics[width=0.8\textwidth]{linux系统播放声音时的具体流程}
\end{figure}

\paragraph{PulseAudio提供的功能}
\begin{enumerate}
    \item 能够独立调节每个软件的音量
    \item 支持音频复用，让用户同时播放多个应用程序的音频
    \item 通过 TCP 服务器传输音频
    \item PulseAudio 的零复制内存架构可让您在应用程序和音频设备之间传输音频数据，无需进行不必要的复制或缓冲，从而实现更低的延迟和更好的资源管理
    \item 为现有应用程序提供兼容层，无需任何修改即可使用
    \item 多种模块提供各类必备功能
\end{enumerate}

\section{PulseAudio常用概念}
\subsection{PulseAudio的结构}
PulseAudio在linux系统中的位置，他所扮演的角色，可以见图\ref{PulseAudio在linux系统中的结构}

面向底层，PulseAudio负责与ALSA进行交互，ALSA source从ALSA获取声音，ALSA sink向ALSA发送声音

面向库文件层和应用层，提供符合UNIX/TCP协议的通信途径，收发音频数据。
\begin{figure}[!ht]\centering
    \caption{PulseAudio在linux系统中的结构}\label{PulseAudio在linux系统中的结构}
    \includegraphics[width=0.8\textwidth]{PulseAudio在linux系统中的结构}
\end{figure}

\subsection{sink和source}
音频数据，如果比作河流。source是河流的源头，是流的开始和起源。sink就是大海，他是音频数据流所流向、所汇聚的地方。但是并不代表收到数据流的个体，不会再往外发送数据，他可能既是某些流的sink，又是某些流的source。

流入大海的流就叫sink-input，从源头流出来的流就叫source-output。

sink就是用来播放的设备，sink-input就是播放的流，播放的过程就是创建一个sink-input，并把它连接到相应的sink上。

source就是录音的设备，source-output就是录音的流，录音的过程就是创建一个source-output，并把它连接到相应的source上。
\begin{figure}[!ht]\centering
    \caption{PulseAudio的sink与source}\label{PulseAudio的sink与source}
    \includegraphics[width=0.6\textwidth]{PulseAudio的sink与source}
\end{figure}

\subsection{modules}
PulseAudio是后台进程，所以他本身是一种daemon。

它还可以根据用户的需求，启动不同的mudules，这些modules实际上就是动态库。

modules和daemons构成的集合，为上层应用层提供服务。

\paragraph{module-alsa-sink / module-alsa-source}
ALSA设备在PulseAudio层面的逻辑抽象，alsa-sink用来回放声音，alsa-source用来录制声音。

\paragraph{module-null-sink / module-null-source}
类似于\verb|/dev/null|，把sink-input连接到null-sink上就好像往黑洞里播放声音，啥也听不见。把source-output连接到null-source上的话，只会录回来一堆静音数据。

\paragraph{module-virtual-sink}
一个虚拟的设备，具体的作用在本文后面的部分再加以说明。

\paragraph{module-sine}
该module会创建一个sink-input，往指定的sink播放正弦波。

\paragraph{module-loopback}
source是用来录制声音的设备，sink是用来播放声音的设备，如果把声音录到以后不多加处理，直接想播出去的话，就可以使用module-loopback了。

\section{命令行工具}
\subsection{pacat}
\verb|pacat|是PulseAudio提供的用来播放和录制声音的命令行工具，跟ALSA下的aplay和arecord的作用来类似，具体的用法可以通过\verb|pacat -h|来查看

\subsection{pactl}
\verb|pactl|是PulseAudio提供的控制命令，可以对正在工作对象进行各种各样的控制。\verb|pactl -h|可以查看帮助手册

\paragraph{pactl list}
列出PulseAudio管理的对象的信息。可以是设备(sink / source)的信息，可以是流的信息(sink-input / source-output)

基本上一个声音对象的所有属性都会体现在这些信息里。

\begin{lstlisting}
pactl list sinks
pactl list sources
pactl list sink-inputs
pactl list source-outputs
\end{lstlisting}

\paragraph{pactl load-module / pactl unload-module}
动态地加载、关闭某个modules

\paragraph{在运行时快速识别和管理接收点}
\begin{lstlisting}
$ pactl set-sink-volume 0 +3%
$ pactl set-sink-volume 0 -3%
$ pactl set-sink-mute 0 toggle
\end{lstlisting}

这些命令分别用于增加、减少音量和切换静音状态

\subsection{pacmd}
\verb|pacmd| 可用于在运行时重新配置 PulseAudio 音频服务器

\paragraph{列出所有可用的输出接收点}
\begin{lstlisting}
pacmd list-sinks | grep -e 'name:' -e 'index:'
\end{lstlisting}

\paragraph{设置系统范围的默认接收点}
在\verb|/etc/pulse/default.pa|文件中添加要设置为默认的接收点名称，比如
\begin{lstlisting}
/etc/pulse/default.pa
...
set-default-sink alsa_output.pci-0000_04_01.0.analog-stereo
...
\end{lstlisting}

或者，在用户级别的\verb|default.pa|文件中设置，该文件会加载\verb|/etc/pulse/default.pa|中的默认配置，并覆盖在此文件中指定的行

\chapter{硬件接口}
\minitoc
\section{V4L2}
Video4Linux 2(通常简写为 V4L2)是 Linux 内核中用于与视频采集硬件(如摄像头、电视卡等)进行交互的接口标准。它取代了早期的 Video4Linux (V4L) 接口，提供了更丰富和灵活的功能集。V4L2 不仅适用于视频捕捉设备，还支持视频输出设备、软件编码器/解码器以及其他类型的多媒体硬件。

统一的 API：V4L2 提供了一组标准化的系统调用和 ioctl 命令，使得应用程序可以以一致的方式访问不同的视频设备，无论这些设备的具体硬件实现如何。

支持流式操作：支持基于内存映射(mmap)、用户指针(userptr)或读/写操作的流式 I/O 模式，允许高效的数据传输。

\paragraph{使用v4l2的简要流程}
\begin{enumerate}
    \item 打开设备文件 (/dev/videoX，其中 X 是设备编号)。
    \item 查询设备能力，了解设备支持的功能和特性。
    \item 设置格式，指定希望使用的图像分辨率、像素格式等。
    \item 请求缓冲区，用于存储即将捕获的数据帧。
    \item 启动流，开始数据采集过程。
    \item 处理数据，从缓冲区中读取并处理每一帧图像。
    \item 停止流 和 释放资源，当不再需要捕获时，关闭设备并清理相关资源。
\end{enumerate}


\end{document}